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.
Odin uses new
and free
to allocate and release a single item, and make
and destroy
to (de)allocate collections.
1s_ptr := new(MyStruct)
2free(s_ptr)
3
4arr := make([dynamic]int)
5delete(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.
1Context :: struct {
2 allocator: Allocator,
3 temp_allocator: Allocator,
4 assertion_failure_proc: Assertion_Failure_Proc,
5 logger: Logger,
6
7 user_ptr: rawptr,
8 user_index: int,
9
10 // Internal use only
11 _internal: rawptr,
12}
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.
1// Overloaded procedure to clone from a string,
2// `[]byte`, `cstring` or a `^byte` + length
3clone_from :: proc{
4 clone,
5 clone_from_bytes,
6 clone_from_cstring,
7 clone_from_ptr,
8}
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.
1// This can be a common idiom in many code bases
2n0, err := caller_2()
3if err != nil {
4 return err
5}
6
7// The above idiom can be transformed into the following
8n1 := caller_2() or_return
9
10
11// And if the expression is 1-valued, it can be used like this
12caller_1() or_return
13// which is functionally equivalent to
14if err1 := caller_1(); err1 != nil {
15 return err1
16}
17
18// Multiple return values still work with `or_return` as it only
19// pops off the end value in the multi-valued expression
20n0, 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.