現在のブログ
ゲーム開発ブログ (2025年~) Gamedev Blog (2025~)
レガシーブログ
テクノロジーブログ (2018~2024年) リリースノート (2023~2025年) MeatBSD (2024年)
【Odin】Odin言語のレビュー(更新済み)
此れまでC言語やC++の代替を目指した言語が沢山登場してきました。
其れでも尚、C言語とC++は世界で最も使われている言語の2位や3位に残り続け、寧ろ利用も増えています。
PythonだけがCとC++を上回っている様です。
此れには非常にちゃんとした理由があります。
C言語とC++は単なるプログラミング言語であって、エコシステム全体ではないからです。
システムプログラミング言語についてあたしが奇妙に思うのは、其れらをペアで並べると其々の主な売りポイントがはっきり見える事です。
- 低レベルかつ多機能:C言語とC++
- メモリ安全性主張:ZigとRust
- Apple中心:Objective-CとSwift
- Google中心若しくはWeb開発寄り:Go言語とCarbon
- ゲーム開発特化:OdinとJai
此れらをグループ化したのは、文法が似ている(C言語とC++、ZigとRust、OdinとJai)か、同じ企業出身(Objective-CとSwift、Go言語とCarbon)だからです。
全部試したわけではないですが、試した物の中で:
- C言語はほぼ何でも、特に組み込み、TUI、デスクトップGUI、CLIプログラムであたしの第一選択言語です。
- C++はゲーム開発のメイン言語ですが、Cでは足りない時に他の用途でも使います。
- Go言語はサーバーデーモンのメイン言語でしたが、Web開発を辞めてからはもう使っていません。
- Zigは一時期好きになろうと頑張った言語です。良い点は多いのですが、極端にドグマティックで、ABIが非常に不安定なので、コンパイラや自動フォーマッタと戦う時間の方が作業時間より多くなり、使うのを辞めました。
コードを見た瞬間に諦めた言語:
- Rustの文法があんまにも暗号的過ぎて、いったい何をやっているのかどう頑張っても理解出来ない。
- Objective-Cの文法が酷過ぎて、見るだけで脳が死ぬ。
その他の理由で使っていない言語:
- SwiftはAppleエコシステムに縛られていた(MacBook1台は遅すぎ、もう1台は死んでいる)。今はWindowsやLinuxでも動くが、EULAが怖い。
- Carbonは4年経っても未だ0.0.0で、全然良い兆候ではない。Google社はいったい何を4年間やってたんだ?オナニーか?しかもソースからビルドが必要で、Googleのコードをソースからビルドする辛さを知っているので試す気にもならない。
- Jaiは未だ一般公開されていないので試せない。
勿論此れらも触れておきます:
- PHP — 最初に触れた言語で、此のブログも此れで書かれている。でもシステム言語ではない。
- アセンブリ言語 — 此れらの言語が本当の姿で隠れている物。主な用途はコード最適化、コンパイラ、ゲームボーイやスーパーファミコンでのプログラミング。
未だ触れていない言語が一つあります:Odin。
理由は、此れだけが「未だ使っていないけど使える」言語だからです。
此処でOdinをレビューし、必要に応じてC言語、C++、Go言語、Zigと比較します。
Odinのメリット
Odinはシンプルさ、高性能、手動メモリ管理とカスタムアロケータ、プログラミングの楽しさ、ドグマフリー、充実した標準ライブラリ、パッケージマネージャなし、LSPなし(一応はある)、高速コンパイルに焦点を当てている様です。
非常に良い売り文句です。
特に目立つのは、ゲーム開発者向けにかなり調整されている点です。
有名なC言語/C++ライブラリが標準ライブラリに含まれるのは素晴らしい。
但し標準ライブラリは3つに分かれているようです:base、core、vendor。
baseは超基本的な物、coreはGo言語が含む物+α、vendorはゲーム開発のよく使われるライブラリ群。
インストール
Odinはあたしが使うほぼ全てのOSとアーキテクチャに対応しています(RISC-Vだけ欠けている)。
まるであたしが作った言語みたいです。
問題:ドキュメント
始める前から最初の問題にぶち当たりました:ドキュメントの不足。
ドキュメント不足はZigをダメにした大きな要因の一つでした(他にも問題は山程ありますが)。
但しウェブサイトに沢山のサンプルがある様なので、あたしには其れで十分です。
こんにちは、世界!
全てのプログラミング言語が最初にやる事:こんにちは、世界!
package main
import "core:fmt"
main :: proc() {
fmt.println("こんにちは、世界!")
}
実行:
$ odin run .
Error: Try using '-microarch:native' as Odin defaults to x86-64-v2 (close to Nehalem), and your CPU seems to be older.
マジかよ?
Core 2 DuoにOdinがない?
最悪だわ。
更新(2026年03月04日):
このポスト によると、Core 2 Duo でのコンパイルが可能との事です。
$ odin run . -microarch:x86-64
こんにちは、世界!
何故デフォルトで古いプロセッサをサポートしないのか理解出来ませんが、回避策があると分かって良かったです。
ではRaspberry Pi 4で試してみましょう。
$ odin run .
こんにちは、世界!
疑う余地なく、此れが一番Go言語に似ています。
パッケージ名を定義する必要がある、importするのは"fmt"、mainに関数型がない、セミコロンなし、printlnがfmtの一部。
Go言語での同一プログラム:
package main
import "fmt"
func main() {
fmt.Println("こんにちは、世界!")
}
因みにGo言語やZigみたいにデフォルトで静的リンクしているのかな?
$ odin build .
$ ls -thal 01.hello
-rwxr-xr-x 1 suwako suwako 362K 3月 4 02:24 01.hello
$ file 01.hello
01.hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (FreeBSD), dynamically linked, interpreter /libexec/ld-elf.so.1,
for FreeBSD 15.0 (1500068), FreeBSD-style, with debug_info, not stripped
$ ldd 01.hello
01.hello:
libm.so.5 => /lib/libm.so.5 (0xd45ee190000)
libc.so.7 => /lib/libc.so.7 (0xd45e9e00000)
libsys.so.7 => /lib/libsys.so.7 (0xd45fd000000)

めっちゃガッカリ・・・
更新(2026年03月04日):
このポスト によると、静的リンクが可能との事です。
$ odin build . -extra-linker-flags:-static
$ ls -thal 01.hello
-rwxr-xr-x 1 suwako suwako 4.9M 3月 4 14:04 01.hello
$ file 01.hello
01.hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (FreeBSD), statically linked,
for FreeBSD 15.0 (1500068), FreeBSD-style, with debug_info, not stripped
$ ldd 01.hello
ldd: 01.hello: not a dynamic ELF executable
デフォルトで静的リンクになっていない事実は変わりませんが、オプションがあると分かって良かったです。
アセンブリはどうなっているか見てみましょう。
$ objdump -d 01.hello | less
...
000000000023d114 <main>:
23d114: d10583ff sub sp, sp, #0x160
23d118: a9157bfd stp x29, x30, [sp, #0x150]
23d11c: b90037e0 str w0, [sp, #0x34]
23d120: f9001fe1 str x1, [sp, #0x38]
23d124: 14000001 b 0x23d128 <main+0x14>
23d128: b94037e9 ldr w9, [sp, #0x34]
23d12c: f9401fe8 ldr x8, [sp, #0x38]
23d130: b9014fe9 str w9, [sp, #0x14c]
23d134: f900a3e8 str x8, [sp, #0x140]
23d138: f940a3e8 ldr x8, [sp, #0x140]
23d13c: f9000be8 str x8, [sp, #0x10]
23d140: 2a0903e8 mov w8, w9
23d144: 93407d05 sxtw x5, w8
23d148: f90007e5 str x5, [sp, #0x8]
23d14c: d503201f nop
23d150: 30e206a8 adr x8, 0x201225 <write+0x201225>
23d154: f90093e8 str x8, [sp, #0x120]
23d158: 52800608 mov w8, #0x30 // =48
23d15c: f90097e8 str x8, [sp, #0x128]
23d160: f94093e0 ldr x0, [sp, #0x120]
23d164: f94097e1 ldr x1, [sp, #0x128]
23d168: 528006c2 mov w2, #0x36 // =54
23d16c: 52800223 mov w3, #0x11 // =17
23d170: aa1f03e4 mov x4, xzr
23d174: 940073ce bl 0x25a0ac <runtime::multi_pointer_slice_expr_error>
23d178: f94007e8 ldr x8, [sp, #0x8]
23d17c: f9400be9 ldr x9, [sp, #0x10]
23d180: 91000108 add x8, x8, #0x0
23d184: f9009be9 str x9, [sp, #0x130]
23d188: f9009fe8 str x8, [sp, #0x138]
23d18c: f9409bea ldr x10, [sp, #0x130]
23d190: f9409fe8 ldr x8, [sp, #0x138]
23d194: d503201f nop
23d198: 1022da49 adr x9, 0x282ce0 <runtime::args__>
23d19c: f900012a str x10, [x9]
23d1a0: f9000528 str x8, [x9, #0x8]
23d1a4: 9102c3e0 add x0, sp, #0xb0
23d1a8: f90017e0 str x0, [sp, #0x28]
23d1ac: 52800e08 mov w8, #0x70 // =112
23d1b0: 2a0803e2 mov w2, w8
23d1b4: f90013e2 str x2, [sp, #0x20]
23d1b8: 2a1f03e1 mov w1, wzr
23d1bc: b90033e1 str w1, [sp, #0x30]
23d1c0: 9400940c bl 0x2621f0 <memset@plt>
23d1c4: f94017e0 ldr x0, [sp, #0x28]
23d1c8: 97ffff48 bl 0x23cee8 <runtime::[core.odin]::__init_context>
23d1cc: f94013e2 ldr x2, [sp, #0x20]
23d1d0: b94033e1 ldr w1, [sp, #0x30]
23d1d4: 910103e0 add x0, sp, #0x40
23d1d8: f9000fe0 str x0, [sp, #0x18]
23d1dc: 94009405 bl 0x2621f0 <memset@plt>
23d1e0: f9400fe8 ldr x8, [sp, #0x18]
23d1e4: 97ffff2d bl 0x23ce98 <runtime::default_context>
23d1e8: f9400fe1 ldr x1, [sp, #0x18]
23d1ec: f94013e2 ldr x2, [sp, #0x20]
23d1f0: f94017e0 ldr x0, [sp, #0x28]
23d1f4: 94009407 bl 0x262210 <memmove@plt>
23d1f8: f94017e0 ldr x0, [sp, #0x28]
23d1fc: 94006ad2 bl 0x257d44 <__$startup_runtime>
23d200: f94017e0 ldr x0, [sp, #0x28]
23d204: 940036da bl 0x24ad6c <main::main>
23d208: f94017e0 ldr x0, [sp, #0x28]
23d20c: 94006bb7 bl 0x2580e8 <__$cleanup_runtime>
23d210: b94033e0 ldr w0, [sp, #0x30]
23d214: a9557bfd ldp x29, x30, [sp, #0x150]
23d218: 910583ff add sp, sp, #0x160
23d21c: d65f03c0 ret
かなり非効率に見えます。
ZigやGo言語程酷くはないけど、C言語やC++よりは明らかに悪い。
Odinは只文字を表示するだけで、裏で大量のメモリ確保や操作、エラーチェックをしている様です。
良くないですね。
更新(2026年03月04日):
このポスト によると、命令数を削減出来るとの事です。
$ odin build . -o:minimal -no-bounds-check -disable-assert
$ objdump -d 01.hello | less
000000000021df70 <main::main>:
21df70: d10203ff sub sp, sp, #0x80
21df74: f9003bfe str x30, [sp, #0x70]
21df78: f90007e0 str x0, [sp, #0x8]
21df7c: 14000001 b 0x21df80 <main::main+0x10>
21df80: f94007e5 ldr x5, [sp, #0x8]
21df84: f90033ff str xzr, [sp, #0x60]
21df88: f90037ff str xzr, [sp, #0x68]
21df8c: 910143e8 add x8, sp, #0x50
21df90: f9002bff str xzr, [sp, #0x50]
21df94: f9002fff str xzr, [sp, #0x58]
21df98: 910103e9 add x9, sp, #0x40
21df9c: d503201f nop
21dfa0: 30f14c0a adr x10, 0x200921 <write+0x200921>
21dfa4: f90023ea str x10, [sp, #0x40]
21dfa8: 5280036a mov w10, #0x1b // =27
21dfac: f90027ea str x10, [sp, #0x48]
21dfb0: f9001be9 str x9, [sp, #0x30]
21dfb4: d2941e29 mov x9, #0xa0f1 // =41201
21dfb8: f2bac8a9 movk x9, #0xd645, lsl #16
21dfbc: f2df55a9 movk x9, #0xfaad, lsl #32
21dfc0: f2e543e9 movk x9, #0x2a1f, lsl #48
21dfc4: f9001fe9 str x9, [sp, #0x38]
21dfc8: f9401bea ldr x10, [sp, #0x30]
21dfcc: f9401fe9 ldr x9, [sp, #0x38]
21dfd0: f9002bea str x10, [sp, #0x50]
21dfd4: f9002fe9 str x9, [sp, #0x58]
21dfd8: f90033e8 str x8, [sp, #0x60]
21dfdc: 52800028 mov w8, #0x1 // =1
21dfe0: f90037e8 str x8, [sp, #0x68]
21dfe4: f94033ea ldr x10, [sp, #0x60]
21dfe8: f94037e9 ldr x9, [sp, #0x68]
21dfec: f90013ea str x10, [sp, #0x20]
21dff0: f90017e9 str x9, [sp, #0x28]
21dff4: f94013e0 ldr x0, [sp, #0x20]
21dff8: f94017e1 ldr x1, [sp, #0x28]
21dffc: f0ffff09 adrp x9, 0x200000 <write+0x200000>
21e000: 9124f529 add x9, x9, #0x93d
21e004: f9000be9 str x9, [sp, #0x10]
21e008: f9000fe8 str x8, [sp, #0x18]
21e00c: f9400be2 ldr x2, [sp, #0x10]
21e010: f9400fe3 ldr x3, [sp, #0x18]
21e014: 52800028 mov w8, #0x1 // =1
21e018: 12000104 and w4, w8, #0x1
21e01c: 9400328f bl 0x22aa58 <fmt::println>
21e020: f9403bfe ldr x30, [sp, #0x70]
21e024: 910203ff add sp, sp, #0x80
21e028: d65f03c0 ret
僅かな削減ではありますが、其れでも未だ命令数が多過ぎます。
但し後で分かったのですが、Odinは静的リンクも出来ます。
$ odin build . -build-mode:static
$ file 01.hello
01.hello: ELF 64-bit LSB executable, ARM aarch64, version 1 (FreeBSD), dynamically linked, interpreter /libexec/ld-elf.so.1,
for FreeBSD 15.0 (1500068), FreeBSD-style, with debug_info, not stripped
・・・結局出来ないみたいです。
修正される事を祈ります。
後コンパイル時間が結構長い事に気づきました。
ZigやC++程ではないですが、C言語やGo言語よりは明らかに遅いです。
Raspberry Piでコンパイルしているからかもしんが、あんま厳しく見ないでおきます。
OpenGL
もう少し実用的な物、例えばOpenGLウィンドウの描画を試してみましょう。
どうせOdinはゲーム開発者向けに作られていると言っているので、其の期待に応えられるか確認しないと。
package main
import gl "vendor:OpenGL"
import "vendor:glfw"
import "core:fmt"
import "core:os"
import "core:c"
framebuffer_cb :: proc "c" (window: glfw.WindowHandle, width: i32, height: i32) {
gl.Viewport(0, 0, width, height)
}
vertexSrc: cstring =
`#version 330 core
layout (location = 0) in vec3 aPos;
void main() {
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);
}`
fragSrc: cstring =
`#version 330 core
out vec4 FragColor;
void main() {
FragColor = vec4(1.f, .5f, .2f, 1.f);
}`
WIDTH :: 500
HEIGHT :: 300
main ::proc() {
glfw.Init()
glfw.WindowHint(glfw.CONTEXT_VERSION_MAJOR, 3)
glfw.WindowHint(glfw.CONTEXT_VERSION_MINOR, 3)
glfw.WindowHint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE)
window := glfw.CreateWindow(WIDTH, HEIGHT, "例え", nil, nil)
defer glfw.Terminate()
if window == nil {
fmt.println("GLFWウィンドウを開くに失敗")
os.exit(-1)
}
glfw.MakeContextCurrent(window)
gl.load_up_to(3, 3, glfw.gl_set_proc_address)
gl.Viewport(0, 0, WIDTH, HEIGHT)
glfw.SetFramebufferSizeCallback(window, framebuffer_cb)
success: i32
infoLog: [512]c.char
vertexShader := gl.CreateShader(gl.VERTEX_SHADER)
gl.ShaderSource(vertexShader, 1, &vertexSrc, nil)
gl.CompileShader(vertexShader)
gl.GetShaderiv(vertexShader, gl.COMPILE_STATUS, &success)
if success != 1 {
gl.GetShaderInfoLog(vertexShader, 512, nil, raw_data(&infoLog))
os.exit(-1)
}
fragShader := gl.CreateShader(gl.FRAGMENT_SHADER)
gl.ShaderSource(fragShader, 1, &fragSrc, nil)
gl.CompileShader(fragShader)
gl.GetShaderiv(fragShader, gl.COMPILE_STATUS, &success)
if success != 1 {
gl.GetShaderInfoLog(fragShader, 512, nil, raw_data(&infoLog))
os.exit(-1)
}
shaderProgram := gl.CreateProgram()
defer gl.DeleteProgram(shaderProgram)
gl.AttachShader(shaderProgram, vertexShader)
gl.AttachShader(shaderProgram, fragShader)
gl.LinkProgram(shaderProgram)
gl.GetProgramiv(fragShader, gl.LINK_STATUS, &success)
if success != 1 {
gl.GetShaderInfoLog(fragShader, 512, nil, raw_data(&infoLog))
os.exit(-1)
}
gl.DeleteShader(vertexShader)
gl.DeleteShader(fragShader)
vertices := [?]f32 {
.5, .5, .0,
.5, -.5, .0,
-.5, -.5, .0,
-.5, .5, .0,
}
indices := [?]u32 {
0, 1, 3,
1, 2, 3,
}
VBO, VAO, EBO: u32
gl.GenVertexArrays(1, &VAO)
defer gl.DeleteVertexArrays(1, &VAO)
gl.GenBuffers(1, &VBO)
defer gl.DeleteBuffers(1, &VBO)
gl.GenBuffers(1, &EBO)
defer gl.DeleteBuffers(1, &EBO)
gl.BindVertexArray(VAO)
gl.BindBuffer(gl.ARRAY_BUFFER, VBO)
gl.BufferData(gl.ARRAY_BUFFER, size_of(vertices),
raw_data(&vertices), gl.STATIC_DRAW)
gl.BindBuffer(gl.ELEMENT_ARRAY_BUFFER, EBO)
gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, size_of(indices),
raw_data(&indices), gl.STATIC_DRAW)
gl.VertexAttribPointer(0, 3, gl.FLOAT, gl.FALSE, 3 * size_of(f32), 0)
gl.EnableVertexAttribArray(0)
gl.BindBuffer(gl.ARRAY_BUFFER, 0)
gl.BindVertexArray(0)
for !glfw.WindowShouldClose(window) {
if glfw.GetKey(window, glfw.KEY_Q) == glfw.PRESS {
glfw.SetWindowShouldClose(window, true)
}
gl.ClearColor(.6, .1, .6, 1.)
gl.Clear(gl.COLOR_BUFFER_BIT)
gl.UseProgram(shaderProgram)
gl.BindVertexArray(VAO)
gl.DrawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, nil)
glfw.SwapBuffers(window)
glfw.PollEvents()
}
}
此れはOdinレビュー記事なのでOpenGLチュートリアルではないので説明は省きます。
コンパイル:
$ odin build .
ld: error: unable to find library -lglfw
clang: error: linker command failed with exit code 1 (use -v to see invocation)
え、GLFWが言語に含まれているのに、システムにライブラリをインストールする必要があるの?
ガッカリポイントです。
まあ最悪ではないですが。
$ doas pkg install glfw
Updating FreeBSD-ports repository catalogue...
FreeBSD-ports repository is up to date.
Updating FreeBSD-ports-kmods repository catalogue...
FreeBSD-ports-kmods repository is up to date.
All repositories are up to date.
The following 1 package(s) will be affected (of 0 checked):
New packages to be INSTALLED:
glfw: 3.4_2 [FreeBSD-ports]
Number of packages to be installed: 1
The process will require 7 MiB more space.
1 MiB to be downloaded.
Proceed with this action? [y/N]: y
[1/1] Fetching glfw-3.4_2: 100% 1 MiB 1.0 M/s 00:01
Checking integrity... done (0 conflicting)
[1/1] Installing glfw-3.4_2...
[1/1] Extracting glfw-3.4_2: 100%
$ odin build .
$ ./02.opengl
動いた!
只此のコードはちょっと判断が難しいです。
かなりGo言語っぽいけどZigの要素も少し混じっていて、基本的にはCコードに色々プレフィックス付けた感じ。
まあCライブラリを使っているわけですし、Zigと同じくCライブラリとの相互運用が出来ている様です。
次はVulkanをやりたい所ですが、長過ぎるしネタバレし過ぎるし時間もなくなってきたので辞めておきます。
総評
Odinを「プログラミングが楽しいC言語」だと思うか? → いいえ
Odinが近いうちにC言語を置き換えると思うか? → いいえ
OdinがZigより優れていると思うか? → はい
Odinに居場所があると思うか? → はい
あたしの見立てでは、Odinは要するに「必要なC言語/C++ライブラリが(マシンにインストールされていれば)最初から入っているGo言語」です。
言語自体は未だ開発中で、まだまだ道のりは長いです。
現段階ではC言語より優れているとは思いません。
ゲーム開発者のうち「ライブラリを手動でダウンロードしたくない人」には居場所があると思います。
其れ以外では、C言語より優先する理由がまだ見つかっていません。
以上