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