Hello, odin

4 April, 2025 - #odin#stream of consciousness


I've been looking at the Odin language for a while, and figured it was time to give it a try. It's not talked about very much, though it looks like it aims to be an improvement on C. I saw Ginger Bill's discussion with TJ and Primeagen a while ago, and he talked clearly and directly about his opinions and its aims for simplicity.

I have no idea what I'm getting into here. The syntax looks sort of similar to that of Jonathan Blow's Jai language that I've seen in his videos.

Download

After downloading it from Github, it looks self-contained.

├───base │ ├───builtin │ ├───intrinsics │ └───runtime ├───bin │ ├───llvm │ │ └───windows │ └───nasm │ └───windows ├───core │ ├───bufio │ ├───bytes │ ├───c │ ├───compress │ │ ├───gzip │ │ ├───shoco │ │ └───zlib │ ├───container │ │ ├───avl │ │ ├───bit_array │ │ ├───intrusive │ │ ├───lru │ │ ├───priority_queue │ │ ├───queue │ │ ├───rbtree │ │ ├───small_array │ │ └───topological_sort │ ├───crypto │ ├───debug │ ├───dynlib │ ├───encoding │ ├───flags │ ├───fmt │ ├───hash │ ├───image │ │ ├───bmp │ │ ├───netpbm │ │ ├───png │ │ ├───qoi │ │ └───tga │ ├───io │ ├───log │ ├───math │ ├───mem │ ├───net │ ├───odin │ │ ├───ast │ │ ├───doc-format │ │ ├───format │ │ ├───parser │ │ ├───printer │ │ └───tokenizer │ ├───os │ ├───path │ ├───prof │ ├───reflect │ ├───relative │ ├───simd │ ├───slice │ ├───sort │ ├───strconv │ ├───strings │ ├───sync │ ├───sys │ ├───testing │ ├───text │ ├───thread │ ├───time │ │ ├───datetime │ │ └───timezone │ └───unicode ├───examples │ ├───all │ └───demo ├───shared └───vendor ├───box2d ├───cgltf ├───commonmark ├───compress ├───darwin ├───directx ├───egl ├───ENet ├───fontstash ├───ggpo ├───glfw ├───libc ├───lua ├───microui ├───miniaudio ├───nanovg ├───OpenEXRCore ├───OpenGL ├───portmidi ├───raylib ├───sdl2 ├───sdl3 ├───stb ├───vulkan ├───wasm ├───wgpu ├───windows ├───x11 └───zlib

It looks like the vendor/ folder comes with a bunch of prebundled third-party libraries for things I could use to build games: Box2D, Lua, Vulkan, GLFW, and so on. I've never seen this before, that's unexpected, but looks very nice if it works.

Simple Programs

After adding the odin directory to $PATH, copying and running the "Hellope!" program builds and runs as advertised. There's no boiler plate, no .toml file or anything in the directory to build and run the program.

package main import "core:fmt" main :: proc() { fmt.println("Hello, World!") }

Semicolons appear optional.

The operators seem familiar to C and C++ with a few new ones:

%% remainder (floored) integers &~ bitwise and-not integers, enums %%= remainder (floored) and assign a %%= b is a = a %% b &~= bitwise and-not and assign a &~= b is a = a &~ b

GLFW setup

Do the libraries in that vendor/ folder work, or are they just for show?

I don't know how to import a library, but fmt is import core:fmt so maybe I can import glfw from that folder with vendor/glfw? It seems to work. Looking at the vendor code, it looks like you can use an import by providing the name before the path.

import glfw "bindings"

I'm looking for glfwInit, looks like the wrapper chops off the front:

PS D:\bin\odin-windows-amd64-dev-2025-04> rg Init | rg glfw vendor\glfw\wrapper.odin:Init :: glfw.Init vendor\glfw\wrapper.odin:InitHint :: glfw.InitHint vendor\glfw\wrapper.odin:InitAllocator :: glfw.InitAllocator vendor\glfw\wrapper.odin:InitVulkanLoader :: glfw.InitVulkanLoader

I can't believe this works:

package main import "core:fmt" import "vendor:glfw" main :: proc() { fmt.println(glfw.Init()) }

It's a bit interesting the integer from init gets interpreted as "true" by the formatter.

PS D:\dev\odin\03_glfw_window> odin run . true

Can I just print the version string? It looks like it's wrapped:

PS D:\bin\odin-windows-amd64-dev-2025-04> rg GetVersionString vendor\glfw\wrapper.odin 112:GetVersionString :: proc "c" () -> string { 113: return string(glfw.GetVersionString()) vendor\glfw\bindings\bindings.odin 112: GetVersionString :: proc() -> cstring ---

That seems to work. I'm not sure which version of that function I'm calling though, the one from wrapper.odin or from bindings.odin.

PS D:\dev\odin\03_glfw_window> odin run . 3.4.0 Win32 WGL Null EGL OSMesa VisualC

GLFW window creation

Can it create a window though? What happens when I try to port the GLFW example directly to Odin?

Looks like main doesn't return a value, just like Ada entry procedures, and requires a separate function to do so, os.exit().

Trying to create and then initialize a window says there's a redeclaration. Perhaps := isn't just assignment, but also declares like in Go?

window :: glfw.WindowHandle; window := glfw.CreateWindow(640, 480, "Hello World", nil, nil) PS D:\dev\odin\03_glfw_window> odin run . D:/dev/odin/03_glfw_window/empty_glfw_window.odin(15:2) Error: Redeclaration of 'window' in this scope at D:/dev/odin/03_glfw_window/empty_glfw_window.odin(14:2) window := glfw.CreateWindow(640, 480, "Hello World", nil, nil)

Ok, so there's also no while loop, only for.

It doesn't work as-is. I look through the OpenGL vendor directory in odin and find a mention about loading OpenGL function pointers. Once I add the appropriate call in gl.load_up_to(4, 5, glfw.gl_set_proc_address), it works:

package main import "core:fmt" import "core:os" import "vendor:glfw" import gl "vendor:OpenGL" main :: proc() { if !glfw.Init() { os.exit(-1) } defer glfw.Terminate() fmt.println("Initialized GLFW") window := glfw.CreateWindow(640, 480, "Hello World", nil, nil) defer glfw.DestroyWindow(window) glfw.MakeContextCurrent(window) gl.load_up_to(4, 5, glfw.gl_set_proc_address) if window == nil { fmt.println("Unable to create window.") os.exit(-2) } for !glfw.WindowShouldClose(window) { gl.ClearColor(0.3, 0.5, 0.7, 1.0) gl.Clear(gl.COLOR_BUFFER_BIT) glfw.SwapBuffers(window) glfw.PollEvents() } fmt.println("Terminated GLFW") }

Example blank GLFW application
Example blank GLFW application

Summary

Odin provided a great out of the box experience. This was just a test run, but I might be returning to Odin for some projects to see how well it does.

References