What Makes Odin Fun?

Date of Release

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.

a screenshot of a tweet that pokes fun at 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.