Enums
Enums define a type that can be one of several named values. They make your code more readable and catch mistakes at compile time.
Defining an Enum
enum Direction {
North
South
East
West
}
fn main() {
var dir:Direction = East
print("Direction: {dir}\n")
}
Members are listed one per line. Each member is automatically assigned an integer value starting from 0.
Using Enums in Conditions
enum Color { Red Green Blue }
fn main() {
var c:Color = Green
if (c == Green) {
print("Go!\n")
} elif (c == Red) {
print("Stop!\n")
} else {
print("Something else\n")
}
}
Enums with Match
Enums and match are a natural pair:
enum Season { Spring Summer Autumn Winter }
fn describe(s:Season) {
match (s) {
Spring: print("Flowers blooming\n")
Summer: print("Sun shining\n")
Autumn: print("Leaves falling\n")
Winter: print("Snow falling\n")
}
}
fn main() {
describe(Spring)
describe(Summer)
describe(Autumn)
describe(Winter)
}
No default needed — you’ve covered all variants.
Explicit Values
Assign specific integer values to members:
enum HttpStatus {
OK = 200
NotFound = 404
ServerError = 500
}
fn main() {
var status:HttpStatus = OK
print("Status: {status}\n") // 200
}
Practical Example: State Machine
Enums are perfect for tracking states:
enum GameState { Menu Playing Paused GameOver }
struct Game {
state:GameState
score:i32
}
ex Game {
fn update() {
match (state) {
Menu: {
print("Press ENTER to start\n")
}
Playing: {
score += 10
print("Playing... Score: {score}\n")
}
Paused: {
print("Game paused. Score: {score}\n")
}
GameOver: {
print("Game Over! Final score: {score}\n")
}
}
}
}
fn main() {
var game = Game{Menu, 0}
game.update()
game.state = Playing
game.update()
game.update()
game.state = Paused
game.update()
game.state = GameOver
game.update()
}
Practical Example: Direction Movement
enum Direction { North South East West }
struct Position {
x:i32
y:i32
}
fn move_pos:Position(pos:Position, dir:Direction) {
var new_pos = pos
match (dir) {
North: new_pos.y = new_pos.y + 1
South: new_pos.y = new_pos.y - 1
East: new_pos.x = new_pos.x + 1
West: new_pos.x = new_pos.x - 1
}
return new_pos
}
fn main() {
var pos = Position{0, 0}
print("Start: ({pos.x}, {pos.y})\n")
pos = move_pos(pos, North)
print("After North: ({pos.x}, {pos.y})\n")
pos = move_pos(pos, East)
pos = move_pos(pos, East)
print("After 2x East: ({pos.x}, {pos.y})\n")
}
Try it — Build a state machine with enums in the Playground.
Expert Corner
Enums compile to C typedef enum — they’re just named integers at runtime. Direction.North is 0, Direction.South is 1, and so on. Zero abstraction cost, zero overhead. A Direction variable is just an int in the generated C.
Members separated by newlines, commas optional: GX prefers newline-separated enum members. This produces cleaner version control diffs — adding or removing a member changes exactly one line, with no trailing comma adjustments. Commas are accepted but not required.
extern enum with @c_prefix is how GX wraps C library enums. For example, Sokol’s key codes:
@c_prefix("SAPP_KEYCODE_")
extern enum sapp_keycode {
SPACE = 32
RIGHT = 262
LEFT = 263
UP = 265
DOWN = 264
}
This generates #define macros to bridge C naming conventions. In your GX code, you write SPACE instead of SAPP_KEYCODE_SPACE. The compiler handles the translation.
Enum members are visible across files: When you define an enum in one file and import it in another, all members are available without qualification. This is resolved in the compiler’s first pass to ensure cross-file visibility works correctly.