Odin and Me ยท Appendix A

Appendix A: Odin quick reference

A one-page reminder of the Odin features we use here. For the whole language, see *Understanding the Odin Programming Language* and the official overview.

Declarations

NAME :: value          // compile-time constant (also procs and types)
x := value              // variable, type inferred
y: f64 = value          // variable, explicit type

Procedures

add :: proc(a, b: int) -> int { return a + b }
divmod :: proc(a, b: int) -> (q: int, r: int) { return a / b, a % b }   // multiple returns
greet :: proc(name: string, excited := false) { }                       // default argument

Basic types

bool; int (the default for anything counted or indexed); f64 (my default float; f32 shows up rarely); string; rune. No implicit conversion between numeric types: convert explicitly: f64(n), int(x).

Control flow

if cond { } else { }
switch v { case a, b: ; case: }        // no fallthrough by default
switch { case x > 0: ; case: }         // no-operand switch = an if/else-if ladder
for i := 0; i < n; i += 1 { }          // three-part, C-style
for cond { }                            // condition-only ("while")
for { }                                 // infinite, `break` to exit
for i in 0 ..< n { }                    // half-open range (excludes n)
for i in 1 ..= n { }                    // closed range (includes n)
defer stmt                              // run stmt at scope exit, reverse order for several defers

Strings, runes, text

len(s)                        // byte length, NOT character count
utf8.rune_count_in_string(s)  // actual character count
for r in s { }                 // decodes UTF-8 correctly, one rune per iteration
s[i:j]                         // byte-offset slice; must land on rune boundaries

core:strings.Builder for building text in a loop instead of repeated + concatenation.

Reading input and parsing

scanner: bufio.Scanner
bufio.scanner_init(&scanner, os.to_reader(os.stdin))
defer bufio.scanner_destroy(&scanner)

bufio.scanner_scan(&scanner)     // false at end of input
bufio.scanner_text(&scanner)     // the line, as a string

value, ok := strconv.parse_int(line)    // (value, ok) -- Odin's "did this work" idiom
value, ok := strconv.parse_f64(line)

Structs and enums

Note :: struct { id: int, title: string, body: string }   // every field has a zero value
n := Note{title = "Shopping"}                               // unset fields are zero

Difficulty :: enum { Easy, Medium, Hard }
switch d {
case .Easy:  // ...
case .Medium: // ...
case .Hard:  // ...
}

Arrays, slices, dynamic arrays, maps

fixed:   [3]int              // fixed-size, inline, no allocation
slice:   []int                // (ptr, len) view; xs[:] makes one from any of the other three
dynamic: [dynamic]Note        // growable

append(&dynamic, value)
ordered_remove(&dynamic, i)    // shifts everything after i down -- keeps order
unordered_remove(&dynamic, i)  // swaps the last element into i -- faster, order not kept

m: map[string]int
m[key] += 1                    // missing key reads as the zero value, no separate check needed

Pointers

p := &x        // address-of
p^              // explicit dereference
p.field         // dereferences automatically through a pointer to a struct
for &n in xs { n.field = 1 }   // &n mutates the element in place; without &, you edit a copy

Randomness

state := rand.create(seed)
context.random_generator = rand.default_random_generator(&state)
n := rand.int_max(20) + 1   // 1 .. 20 inclusive -- int_max(n) is exclusive of n

Error handling idiom

value, ok := might_fail()      // (T, bool) -- the common case
if !ok { return }

a := might_fail() or_return     // shortcut: bail out with the same failure, immediately

when conditional compilation

when ODIN_OS == .Windows {
	// only compiled on Windows
} else {
	// only compiled everywhere else
}

Imports themselves can't live inside a when block: import unconditionally, gate usage inside the when.

context

context = runtime.default_context()   // first line of every exported "c"-convention proc
context.random_generator = ...        // where rand.* looks for its generator
context.allocator                     // the default allocator most calls use implicitly

Formatted output

fmt.println(...)                 // args, space-separated, newline
fmt.print(...)                   // no newline
fmt.printf("%d %s %.1f", ...)    // %v is "default format", works on almost anything
fmt.printfln("%d", n)            // printf + trailing newline

%7d zero-pads by default in Odin (0000042), unlike C. Space-pad with % 7d.

Files

data, err := os.read_entire_file_from_path(path, context.allocator)
err := os.write_entire_file(path, some_bytes)   // result must be handled

ANSI escapes (Chapter 7 onward)

\x1b[2J          clear screen
\x1b[H            cursor to top-left
\x1b[{r};{c}H     cursor to row r, column c
\x1b[31m          set colour (SGR code)
\x1b[0m           reset colour