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.

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.

core/runtime/core.odin
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.

core/strings/strings.odin
// 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.