Quick reference guide for Odin. Essential syntax, types, control flow, and common patterns for the Odin programming language.
Hello World
package main
import "core:fmt"
main :: proc() {
fmt.println("Hello, World!") // Odin keeps it clean and readable.
}
Variables
x: int = 42
name: string = "Alice"
flag: bool = true
pi: f64 = 3.14
// Type inference with :=
y := 100 // Compiler infers int
text := "Hello" // Compiler infers string
// Multiple declarations
a, b, c: int = 1, 2, 3
Control Flow
If/Else
if x > 10 {
fmt.println("x is big")
} else {
fmt.println("x is small")
}
if x > 10 {
fmt.println("big")
} else if x > 5 {
fmt.println("medium")
} else {
fmt.println("small")
}
// If as expression
result := "big" if x > 10 else "small"
For Loops
for i in 0..5 { // 0 to 4, exclusive
fmt.println(i)
}
for i in 0..=5 { // 0 to 5, inclusive
fmt.println(i)
}
nums := []int{1, 2, 3}
for n in nums {
fmt.println(n)
}
for n, i in nums { // Value and index
fmt.printf("%d: %d\n", i, n)
}
// Traditional C-style
for i := 0; i < 5; i += 1 {
fmt.println(i)
}
While Loops
y := 10
for y > 0 {
y -= 1
fmt.println(y)
}
// Infinite loop
for {
if y <= 0 {
break
}
y -= 1
}
Functions
add := fn(a: int, b: int) -> int {
return a + b
}
fmt.println(add(2, 3))
// Named function
add :: proc(a: int, b: int) -> int {
return a + b
}
// Multiple return values
divide :: proc(a: f64, b: f64) -> (f64, bool) {
if b == 0 {
return 0, false
}
return a / b, true
}
result, ok := divide(10.0, 2.0)
if ok {
fmt.println(result)
}
// Procedures (no return value)
greet :: proc(name: string) {
fmt.printf("Hello, %s!\n", name)
}
Arrays / Slices
Slices
nums: []int = [1, 2, 3] // Slice literal, mutable and resizable
for n in nums {
fmt.println(n)
}
// Append to slice
append(&nums, 4)
append(&nums, 5, 6)
// Length and capacity
len(nums) // Current length
cap(nums) // Capacity
// Make slice with capacity
nums := make([]int, 0, 10) // Length 0, capacity 10
Arrays
arr: [3]int = {1, 2, 3} // Fixed-size array
arr := [3]int{1, 2, 3} // Shorthand
// Array size inference
arr := [?]int{1, 2, 3} // Compiler infers size 3
Maps
import "core:map"
person := make(map[string]string)
map.set(&person, "name", "Alice")
map.set(&person, "age", "30")
name, found := map.get(&person, "name")
if found {
fmt.println(name)
}
// Map literal
person := map[string]string{
"name" = "Alice",
"age" = "30",
}
Strings
text := "Hello, World!"
text := `Multiline
string
literal`
// String operations
len(text) // Length in bytes
fmt.printf("%s\n", text) // Print formatted
// String concatenation
greeting := "Hello, " + name
// String formatting
name := "Alice"
greeting := fmt.tprintf("Hello, %s!", name)
Structs
Person :: struct {
name: string,
age: int,
}
person := Person{
name = "Alice",
age = 30,
}
fmt.printf("%s is %d\n", person.name, person.age)
// Methods
Person :: struct {
name: string,
age: int,
}
greet :: proc(p: Person) {
fmt.printf("Hello, I'm %s\n", p.name)
}
person := Person{name = "Alice", age = 30}
greet(person)
Enums
Direction :: enum {
Up,
Down,
Left,
Right,
}
dir := Direction.Up
switch dir {
case .Up:
fmt.println("Going up")
case .Down:
fmt.println("Going down")
case .Left:
fmt.println("Going left")
case .Right:
fmt.println("Going right")
}
Unions
Value :: union {
int,
f64,
string,
}
v: Value = 42
v = 3.14
v = "hello"
switch v in v {
case int:
fmt.printf("Integer: %d\n", v)
case f64:
fmt.printf("Float: %f\n", v)
case string:
fmt.printf("String: %s\n", v)
}
Error Handling
import "core:fmt"
// Using optional types
divide :: proc(a: f64, b: f64) -> (result: f64, ok: bool) {
if b == 0 {
return 0, false
}
return a / b, true
}
result, ok := divide(10.0, 2.0)
if ok {
fmt.printf("Result: %f\n", result)
} else {
fmt.println("Division by zero")
}
// Using error types
Error :: enum {
None,
Division_By_Zero,
Invalid_Input,
}
divide :: proc(a: f64, b: f64) -> (f64, Error) {
if b == 0 {
return 0, .Division_By_Zero
}
return a / b, .None
}
File Operations
import "core:os"
import "core:fmt"
// Reading
data, ok := os.read_entire_file("file.txt")
if ok {
fmt.println(string(data))
}
// Writing
content := "Hello, World!"
os.write_entire_file("file.txt", transmute([]u8)content)
Memory Management
import "core:mem"
// Allocators
allocator := context.allocator
// Allocate memory
ptr := new(int)
ptr^ = 42
// Free memory
free(ptr)
// Using different allocators
temp_allocator := mem.temp_allocator()
Tips
- Odin is strongly typed; type declarations are explicit.
- Loops often use ranges like
0..5(exclusive of 5); use0..=5for inclusive.
fmt.scanforread_stringcan handle user input:import "core:fmt" import "core:os" input: [256]u8 n, _ := os.read(os.stdin, input[:]) text := string(input[:n])
- Slices (
[]Type) are dynamic arrays, arrays ([N]Type) are fixed-size.
- Functions are first-class and can be assigned to variables.
- Use
:=for type inference,:for explicit typing.
- Procedures (
proc) are functions; usefnfor function values.
- Multiple return values are common; use tuples or named returns.
- Use
^to dereference pointers,&to take addresses.
- Odin uses explicit memory management; understand allocators for advanced usage.
- Struct literals use
=for field assignment:Person{name = "Alice"}.
- Use
switchfor pattern matching on enums and unions.
- Import packages with
import "package:name"; core library uses"core:"prefix.