👼

神話のプログラム言語 Odin 2025年の変更点のまとめ

に公開

1年近くプログラムは触っていませんでしたが、久しぶりに何か記事でも書こうかと思い、Odinを触ってみたのですが、あれ?動かない!と思って調べて見たところ、最新のリビジョンから仕様が若干変わっていました。
今回は2025年1月以降から変わった変更点について、説明を記載していきます。

環境

私のPC環境はWindowsで動作させています。

  • OS: Windows11
  • Odin dev-2025-12a-nightly
  • Microsoft visual C++ 2022

Odin言語の変更点

1. @(init)、@(fini)で呼び出される関数の扱い

2025年09月以降のリビジョンから、属性(Attributes)の@(init)@(fini)を指定した関数には、contextlessを付けなくてはいけなくなりました。
この処置は、オーバーライドする方法がないため、使用しているアロケーターなどを明示的に強制するという考えのもと変更されています。

以前、VisualStudioのコンソールの日本語文字化け対応の為に、以下のOdinファイルを作成しましたが、procの後に、contextless指定が必須になりました。これにより、fmtライブラリ等のアロケーターを行う関数を呼ぶ場合は、以下の様にcontextを再設定しなければ、いけなくなりました。

windows_setting.odin
// @(init)はmain関数より先に処理されると言う意味
// @(fini)はmain関数の終わりに処理されると言う意味
// init,fini属性が指定された関数では、contextlessを指定する必要
@(init)
output_UTF8 :: proc "contextless" () {
//                   ^^^^^^^^^^^ <- procの後に"contextless"が必須になりました
  context = runtime.default_context()   // fmtなどのライブラリを呼ぶ場合は、contextを再度設定し直す必要がある

  windows.SetConsoleOutputCP(windows.CODEPAGE.UTF8)
}

関数をcontextlessにしてしまうと、暗黙のcontextが利用出来なくなります。ですので、利用する場合は、関数のはじめにruntime.default_context()を呼び出す必要があります。fmtライブラリなどは、アロケーターする関数も含まれています。
@(init)属性(Attributes)とは、プログラム実行時に、main関数より先に呼び出される関数を指します。逆に、@(fini)属性(Attributes)とは、main関数が終了した後に、呼び出される関数を指します。

2. 動的エリアの初期化

2025年02月以降のリビジョンから、動的エリアに対して初期値を設定出来なくなりました。
動的エリアとは、map[dynamic]型名などを指し、これらは、通常動的エリアを設定した時点で、{}を使って、値を設定出来ましたが、今回から出来なくなりました。

sample2.odin
#+feature dynamic-literals  // ソースの先頭に、mapなどの動的型の複合リテラルはデフォルトで無効にする設定を行うと、従来の動的エリアへの初期値を設定出来る
package main

import "core:fmt"

main :: proc() {
  // 従来の動的エリア(map,[dynamic]typeなど)に対して初期値を設定
  some_map := map[string]int {"A" = 1, "C" = 9, "B" = 4} // 動的mapに対して初期値を設定
  defer delete(some_map)

  delete_key( &some_map, "B" )  // mapの部分削除
  fmt.println( some_map )
  // 上記の様な初期設定を継続したい場合は、暫定処置として `#+feature dynamic-literals`をファイルの先頭に記述する必要がある

  // 変更後の動的エリア(map,[dynamic]typeなど)に対して初期値を設定
  s_map := make( map[string]int )
  defer delete( s_map )
  s_map["A"] = 1
  s_map["C"] = 9
  s_map["B"] = 4
//  s_map = { "A" = 1, "C" = 9, "B" = 4 } // または、左記のような記載も可能

  delete_key( &s_map, "B" )  // mapの部分削除
  fmt.println( s_map )
}

動的エリアに対して、初期値を設定出来なくなりました。もし、従来の様に設定したいのであれば、ソースの先頭に#+feature dynamic-literalsを付ける必要があります。但し、これは暫定処置との事です。

3. ゼロ割り算術などの未定義動作(UB:undefined behavior)の扱い

2025年09月以降のリビジョンから、ゼロ割り算術に対して、一定の対応が取られる事になりました。
以下の動画に、からのメッセージがあります。
https://www.youtube.com/watch?v=k8iCkbbBHyg
※ちょっと、動画のサムネイル画像が間抜け面ですが、ですから気にしないで下さい。

sample3.odin
// #+feature integer-division-by-zero:zero
// -integer-division-by-zero:zero
package main

import "base:intrinsics"
import "core:fmt"
import "core:sys/windows"

// VSCodeコンソールの文字化け対策 (windowsのみ)
@(init) output_UTF8 :: proc "contextless" () { windows.SetConsoleOutputCP(windows.CODEPAGE.UTF8) }

main :: proc() {
  fmt.println("zero割りテスト")
  a := 5.2
  b := 0.0
  c := div_zero( a, b )
}

// ジェネリックによる割り算
div_zero :: proc(a, b: $T) -> T 
    where intrinsics.type_is_numeric(T) {   // where条項 typeが数字かのチェック

  if b == 0 {   // integer-division-by-zeroは整数のみ対応なので、処理としては、これの方がいい
    fmt.println("zeroでした")
    return 0
  }
  return a / b
}

上記プログラムにおいて、ゼロ除算が発生しそうなソースの先頭に、#+feature integer-division-by-zero:zeroを付けておけば、ゼロ除算時に、0を返すようになります。
または、以下のように、コンパイル時に-integer-division-by-zero:zeroを付ける事で、ゼロ除算した場合に、0を返す事が可能です。

実行時
odin run .\sample -integer-division-by-zero:zero

注意点は、intのみ対応と言う事です、これがfloatの場合は、従来と同じ結果になります。
ですので、関数内で分母が0かの判断はした方がいいと私は思います。

4. cstring16の扱い

2025年09月以降のリビジョンから、cstring16型が加わりました。これはWin32APIの関数を呼び出す場合に、使用されます。例えば、以下のように、メッセージボックスだけを表示するプログラムで確認します。

sample4.odin
package main

import "core:fmt"
import "core:sys/windows"

// VSCodeコンソールの文字化け対策 (windowsのみ)
@(init) output_UTF8 :: proc "contextless" () { windows.SetConsoleOutputCP(windows.CODEPAGE.UTF8) }

// メッセージボックスの表示例
main :: proc() {
  using windows       // Win32API関数名が長いので、はじめにusingを使って、windows.を省略させています

  // 従来のメッセージボックス表示
  msg := "utf8_to_wstringで表示している"
  title := "タイトル"
  // 関数名のWはワイド文字を表す。逆にAはANSI文字
  rtn := MessageBoxW( nil, utf8_to_wstring(msg), utf8_to_wstring(title), MB_OK | MB_ICONEXCLAMATION )
  fmt.printfln("戻り値 %v", rtn)

  // cstring16を利用したメッセージボックス表示
  // temp_allocatorを使わずに[^]ui16に変換
  msg2 := cstring16("cstring16で表示している")  // 多分win32APIのL"..."と同じ事なんだろうと思う
  title2 := cstring16("タイトル")
  rtn = MessageBoxW( nil, msg2, title2, MB_OK | MB_ICONEXCLAMATION )
  fmt.printfln("戻り値 %v", rtn)
}

従来だと、utf8_to_wstring関数でWCHAR型に変更していましたが、この関数はtemp_allocatorからメモリを割り当て、値を返しているんですが、今回からは、固定文字列をcstring16型にキャストするだけで、そのままwin32API関数へ渡す事が可能になりました。
これはWin32APIのL"..."と同じ動作だと思いますが、詳しくはわかりません。

5. curlライブラリの追加

2025年12月のリビジョンからcURLライブラリが追加されました。
使い方は、以下の通りです。

sample5.odin
package main

import "vendor:curl"  // vender内のcurlライブラリを呼び出す

main :: proc() {
  cURL := curl.easy_init()
  // curlのセットアップ
  curl.easy_setopt( cURL, .URL, "https://qiita.com/" )
  curl.easy_setopt( cURL, .SSL_VERIFYPEER, 0 )
  // 実行
  curl.easy_perform( cURL )
  // 後始末
  curl.easy_cleanup( cURL )
}

私自身、cURLは詳しくないので、[C言語] curl libを使ってみるを参考にして、書いてみました。
まあ、ほとんど、C言語と呼び出す関数は同じなんですけどね。

今後の変更点

1. core:os/os2がosに統合されます

来年初頭(1Q:4月頃)から「core:os/os2」は廃止され、「core:os」に統合される予定です。
Moving Towards a New "core:os"を参照して下さい。
変更点は、ファイルアクセス時に、アロケーターを明確に記載する事と、以下のファイルポインタやエラー情報の変更が加わります。

変更点
// 【ファイルハンドラー】
// 現状
fd: os.Handle
// 新規 HandleからFileポインタに変更されます
f: ^os.File

// 【エラー情報】
// 現状
err: os.Errno // just an integer
// 新規 HandleからFileポインタに変更されます
err: os.Error // a union of enums

2. 先の話ですが、httpとnbioが追加される予定です

何時になるかわかりませんが、下記の「odin-http project」がodinのコアに組み込まれる予定です。
この「odin-http project」の中には、nbioと言うノンブロッキングIOが入っており、これにより、Zigで行っているような非同期処理(async)プログラムが可能になります。
https://github.com/laytan/odin-http

3. もっと先の話ですが、差分コンパイルを実装する予定です

https://youtu.be/E_uIJ8PVIwk
神自身はやりたくないそうですが、神界の同僚からビルド時間を、もっと早くしたいとの仰せがあったらしいです。今でも小さなプログラムであればコンパイル時間は早いのですが、数万行とかになるとそれなりに遅いのかもしれません。実際、プログラムを作っている者にすれば、日にコンパイルは100回以上は行うでしょうから、短くなればそれだけ、他の事に時間を費やす事ができます。

おわりに

次回はデバック操作について記載していきます。
Odinのデバックが難しいらしいので、わかりやすく説明します。

Discussion