What Makes Odin Fun?
Since I got into Odin and started talking about it a lot, I've been asked a couple times what it is that draws me to it. I usually just end up listing a few features that I like. Maybe not the most effective answer, but how the hell do you even explain why something is fun? Anywho, this post is going to be me listing features I like.
Just in more detail than I would in a twitter DM (and with a few examples).
no garbage collection
I'm not a GC purist. I like Go fine and it has a GC. But I do enjoy Odin's manual memory management. It has helped me better internalize how memory works (dangling pointers, yay!). And it actually feels manual, unlike Rust.
Rust has manual memory management the way my truck has a manual transmission because I have to put it in drive.
— Dogue (@magnum_d1ngus) January 5, 2024
*send tweet*
Odin uses new
and free
to allocate and release a single item, and make
and destroy
to (de)allocate collections.
s_ptr := new(MyStruct)
free(s_ptr)
arr := make([dynamic]int)
delete(arr)
no struct methods
Much like GC, this isn't a matter of better or worse for me. It's just different from what I'm used to and I enjoy that. GingerBill has a section in the Odin FAQ explaining his reasoning behind this. While I'm not strongly opinionated on the matter, I can agree with his thinking.
context system
Odin features a system where a context struct is implicitly passed to all functions. This context can set a logger, an allocator, and more allowing you to vary certain behaviors even in third-party packages without having to modify the package source.
Context :: struct {
allocator: Allocator,
temp_allocator: Allocator,
assertion_failure_proc: Assertion_Failure_Proc,
logger: Logger,
user_ptr: rawptr,
user_index: int,
// Internal use only
_internal: rawptr,
}
See the full breakdown in the overview
procedure groups
Odin allows you to group related procedures under a common name. Calling the procedure group will in turn call the procedure in the group that matches the passed parameters. I'm cool with FP-style function overloading, but I like that Odin is explicit about it.
// Overloaded procedure to clone from a string,
// `[]byte`, `cstring` or a `^byte` + length
clone_from :: proc{
clone,
clone_from_bytes,
clone_from_cstring,
clone_from_ptr,
}
packages
They're just directories and a declaration at the top of the file. Odin makes no distinction regarding a "main package" (looking at you, Go), only a main procedure. This means if, like me, you like to play with a library while you're developing it, you can just throw a main :: proc()
in your library and remove it later. ggez
defer
Not much to say here. It's similar to Go, except that it's based on end-of-scope rather than end-of-function. Defer in the Odin overview doc
or_return & or_else
These are just really nice conveniences to have. You can almost think of them as ?
and .unwrap_or_else()
in Rust.
// This can be a common idiom in many code bases
n0, err := caller_2()
if err != nil {
return err
}
// The above idiom can be transformed into the following
n1 := caller_2() or_return
// And if the expression is 1-valued, it can be used like this
caller_1() or_return
// which is functionally equivalent to
if err1 := caller_1(); err1 != nil {
return err1
}
// Multiple return values still work with `or_return` as it only
// pops off the end value in the multi-valued expression
n0, n1 = caller_3() or_return
See the overview for the full example.
conclusion
All in all, despite the Go-like syntax, Odin feels closer to C than most of the other languages claiming to be a better C.
I like C. I just don't like writing C.
Writing Odin is like writing C but with the safety on the footgun engaged.