Types
💡 Best Practices Summary
-
Use
b32instead ofboolwhen creating structs sent to Vulkan or C libraries. -
Use
bit_setinstead of rawu32bitmasks for cleaner engine flags. -
Use
#align(16)when matching Vulkan Uniform Buffer Objects (UBOs).
Types in Odin
Odin features a strong, explicit, data-oriented type system. There are no implicit type conversions, giving you total layout control—essential for C ABI bindings, memory packing, and Vulkan GPU data structures.
🔢 1. Basic & Interop Types
Odin uses explicit bit-width types. Avoid int when defining hardware/Vulkan structures—use sized types instead.
// Integers (Signed & Unsigned)
i8, i16, i32, i64, i128
u8, u16, u32, u64, u128
// Floats & Booleans
f32, f64
bool // 1 byte
b32 // 4 bytes (Standard for C ABI / Vulkan bools)
// Raw Pointers
rawptr // Equivalent to void* in C
🧱 2. Structs & Layout Control
Structs are ordered collections of variables. By default, Odin orders fields in memory sequence, but allows explicit alignment and packing for GPU compatibility.
// Standard Struct
Vector3 :: struct {
x: f32,
y: f32,
z: f32,
}
// Memory-Packed Struct (No padding between fields - good for network packets)
PackedData :: struct #packed {
id: u8, // 1 byte
val: u32, // 4 bytes (normally padded to offset 4, now at offset 1)
}
// Explicit Alignment (Crucial for Vulkan Uniform Buffers / std140 / std430 alignment)
GPUUniforms :: struct #align(16) {
model_matrix: [16]f32,
color: [4]f32,
}
🏷️ 3. Distinct Types (Type Safety Hacks)
A distinct type creates a brand new type with the same memory layout as the underlying type, but the compiler forbids mixing them without an explicit cast.
distinct to prevent accidentally passing an EntityID into a function expecting a TextureID, even if both are internally u32.EntityID :: distinct u32
TextureID :: distinct u32
player_id: EntityID = 10
tex_id: TextureID = 10
// ERROR at compile-time: Cannot mix distinct types!
// player_id = tex_id
// Must be explicitly cast:
player_id = EntityID(tex_id)
🏷️ 4. Unions & Tagged Unions (Type-Safe Polymorphism)
Odin unions are tagged unions by default. They store a value and automatically keep track of which type is currently stored inside.
ShapeCircle :: struct { radius: f32 }
ShapeBox :: struct { width, height: f32 }
// Tagged Union
Shape :: union {
ShapeCircle,
ShapeBox,
}
draw_shape :: proc(shape: Shape) {
// Type switch on the tagged union
switch s in shape {
case ShapeCircle:
// 's' is automatically typed as ShapeCircle here!
fmt.println("Circle radius:", s.radius)
case ShapeBox:
fmt.println("Box dimensions:", s.width, s.height)
case:
fmt.println("Empty shape")
}
}
🎛️ 5. Enums & Bit Sets
Enums
Enums represent a discrete list of named values. You can specify the underlying integer storage type.
RenderMode :: enum u32 {
Forward,
Deferred,
Raytraced,
}
Bit Sets (Cleaner Flag Masks)
Instead of manually doing bitwise ORs (FLAG_A | FLAG_B) like in C, Odin uses bit_set over an enum.
VulkanFeatureFlag :: enum {
ValidationLayers,
RayTracing,
MeshShaders,
}
// Creates a type-safe bitfield
VulkanFeatures :: bit_set[VulkanFeatureFlag; u32]
// Usage:
features: VulkanFeatures = {.ValidationLayers, .MeshShaders}
// Check flags cleanly:
if .RayTracing in features {
// Raytracing is enabled
}
🔗 Related Notes
- Variables — Variable declaration syntax and explicit typing.
- Functions — Passing structs by pointer (
^T) or#by_ptr. - Precision — Sized integers and IEEE 754 float limits.
- Vulkan Architecture Overview — Setting up memory-aligned structures for Vulkan UBOs.
- Basics Of NetWorking — Using
#packedstructs for binary network packet definitions. - Game Development Hub — Central game development index.