Trying Odin (with a playground)

In my recent search for a "better C" alternative, I've looked at C++, D, Rust, Nim, Hare, Odin, and Zig. At first glance, Odin and Zig seem to be exactly what I'm looking for. C++, D, Rust and Nim are bloated with features, while Hare is too bare-bones even for me.

Why do I need a better C when I have Go, you may ask. Well, Go is great at everything except C interop (which I sometimes need). It also has GC (which I sometimes don't need).

I think of Go as "better Java". It'd be nice to have better C, too.

I'll leave Zig for later and explore Odin for now.

Odin has a unique set of characteristics:

  • Simple language design without bells and whistles.
  • Manual memory management with custom allocators.
  • Well-thought standard library.
  • Concise and calm syntax.

Some examples

A classic "hello world":

package main

import "core:fmt"

main :: proc() {
    fmt.println("Hellope!")
    // Hellope!
}

Dynamic arrays and sorting:

package main

import "core:fmt"
import "core:slice"

main :: proc() {
    list := [dynamic]int{11, 7, 42}
    defer delete(list)

    append(&list, 2, 54)
    slice.sort(list[:])

    fmt.println(list)
    // [2, 7, 11, 42, 54]
}

Memory management in Odin is manual, so allocated memory must be explicitly freed. There are two built-in allocators (a heap allocator and a growing arena based allocator), but the language supports custom allocators:

// tracking allocator for debugging
track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator)

// context is an implicit variable
// available in every scope
context.allocator = mem.tracking_allocator(&track)

// we can use a custom allocator at any level,
// even at individual statements
list := make([]int, 6, context.allocator)

Structs, procedures and iteration (I'll skip the "package" stuff from now on):

Person :: struct {
    name: string,
    age: int,
}

person_to_str :: proc(p: Person) -> string {
    return fmt.tprintf("%v - %v", p.name, p.age)
}

people := []Person{
    Person{"Alice", 25},
    Person{"Bob", 24},
    Person{"Cindy", 26},
}

for p, idx in people {
    fmt.println(idx, person_to_str(p))
}
// 0 Alice - 25
// 1 Bob - 24
// 2 Cindy - 26

There are no functions or struct methods, only procedures.

Pointers are declared with a leading caret, and dereferenced with a trailing caret:

val := "Hellope!"

ptr: ^string
ptr = &val

fmt.println(ptr^)
// Hellope!

A bit unusual, but logical.

Errors are just values:

Error :: enum {
    None,
    Insufficient_Funds,
}

withdraw :: proc(balance, amount: int) -> (int, Error) {
    if amount > balance {
        return balance, .Insufficient_Funds
    }
    return balance - amount, .None
}

balance, err := withdraw(42, 1000)
if err != nil {
    fmt.println(err)
}
// Insufficient_Funds

There is also a shortcut for the dreaded if err != nil return:

balance := withdraw(42, 1000) or_return

Generics (aka parametric polymorphism):

Pair :: struct($T: typeid) {
    first: T,
    second: T
}

pair_to_str :: proc(p: $T/Pair) -> string {
    return fmt.tprintf("%v-%v", p.first, p.second)
}

p1 := Pair(int){1, 2}
p2 := Pair(string){"one", "two"}

fmt.println(pair_to_str(p1))
// 1-2

fmt.println(pair_to_str(p2))
// one-two

Here pair_to_str only allows types that are specializations of the Pair type.

Language overview

Trying Odin

The easiest way to get a taste of Odin is the playground I made (it powers the examples in this article).

If you prefer local setup, the official site has a decent "Getting Started" guide. There are binaries for Windows, Linux and macOS, but unfortunately only for amd64 (x86_64).

If you have an arm64 Mac, you can use a Docker image (courtesy of Yeongju Kang with minor modifications by me).

Specialization

Although Odin is a general-purpose language, it leans slightly towards game and visual effects programming. This is probably due to the fact that the language creator (Ginger Bill) is a physicist working in a visual effects company.

Odin supports a native matrix type and matrix operations, and has various SIMD/SIMT-related programming features (of which I don't know anything about, so better consult the doc for those).

As far as I can tell, a significant number of programmers using Odin are game developers.

Current status

Odin is pre-1.0. Sadly, it does not even have a 1.0 roadmap. It also seems to be less popular than Zig or Nim, probably on par with Hare.

However, Odin is actively used in production by the company it's creator works for.

So I hope it has a bright future!

──

Interactive examples in this post are powered by codapi — an open source tool I'm building. Use it to embed live code snippets into your product docs, online course or blog.

 Subscribe to keep up with new posts.