Variables & Constants

Every program needs to store data. GX gives you two tools: var for values that change, and const for values that don’t.

Variables with Type Inference

GX can figure out the type from the value you assign:

fn main() {
    var x = 10          // i32 (integer)
    var pi = 3.14       // f32 (float)
    var name = "Alice"  // str (string)
    var alive = true    // bool

    print("x = {x}\n")
    print("pi = {pi}\n")
    print("name = {name}\n")
    print("alive = {alive}\n")
}

The compiler sees 10 and knows it’s an i32. Sees 3.14 and knows it’s an f32. You don’t have to spell it out.

Explicit Types

Sometimes you want to be specific:

fn main() {
    var x:i32 = 100
    var y:f64 = 3.14159265358979
    var big:i64 = 9999999999
    var name:str = "GX"

    print("x = {x}\n")
    print("y = {y}\n")
    print("big = {big}\n")
    print("name = {name}\n")
}

The type comes right after the variable name with a colon: var x:i32. No space needed (though you can add one if you prefer).

Reassignment

Variables declared with var can be changed:

fn main() {
    var score = 0
    print("Score: {score}\n")

    score = 100
    print("Score: {score}\n")

    score = score + 50
    print("Score: {score}\n")
}

Constants

Use const for values that should never change:

const MAX_PLAYERS = 64
const PI = 3.14159265358979
const GAME_TITLE = "Space Blaster"

fn main() {
    print("Title: {GAME_TITLE}\n")
    print("Max players: {MAX_PLAYERS}\n")
    print("PI = {PI}\n")
}

Constants can also have explicit types:

const MAX_HEALTH:i32 = 100
const GRAVITY:f64 = 9.80665

Try to assign to a constant and the compiler will stop you:

const X = 10
fn main() {
    X = 20  // Error: Cannot assign to constant
}

Constants Build on Constants

Constants can reference other constants — the compiler evaluates everything at compile time:

const WIDTH = 1920
const HEIGHT = 1080
const AREA = WIDTH * HEIGHT
const HALF_WIDTH = WIDTH / 2

fn main() {
    print("Resolution: {WIDTH}x{HEIGHT}\n")
    print("Area: {AREA}\n")
    print("Half width: {HALF_WIDTH}\n")
}

The generated code contains the final numbers (2073600, 960) — no multiplication happens at runtime.

Try it — Define your own constants and use them in the Playground.

Local Constants

Constants work inside functions too:

fn main() {
    const LIMIT = 5

    for (i = 0:LIMIT) {
        print("{i} ")
    }
    print("\n")
}

Expert Corner

Constant folding is not just a convenience — the compiler has a full constant evaluator that handles arithmetic, bitwise ops, comparisons, unary operators, and references to other constants. When you write const TAU = PI * 2.0, the compiler computes 6.28318... and emits the literal value in the generated C. Zero runtime cost.

Why var is mutable by default: GX takes the pragmatic approach. Most local variables get modified (loop counters, accumulators, state). Making mutability the default avoids the let mut ceremony of Rust or the const everywhere pattern of modern C++. If you want immutability, use const — it’s one word and it’s enforced.

Global constants emit as static const in C, which avoids multiple-definition errors across translation units. Local constants emit as const — the C compiler can further optimize them away entirely.