Variables and Types

Gruel is statically typed—every variable has a type known at compile time.

Integer Types

Gruel has the integer types you'd expect:

fn main() -> i32 {
    // Signed integers: i8, i16, i32, i64
    let x: i32 = 42;
    let big: i64 = 1000000000000;

    // Unsigned integers: u8, u16, u32, u64
    let index: u64 = 0;
    let byte: u8 = 255;

    @dbg(x);
    x
}

The number after i or u is the bit width. Signed integers (i) can be negative; unsigned integers (u) cannot.

Type Inference

You don't always need to write types explicitly. The compiler can often infer them:

fn main() -> i32 {
    let x = 42;        // inferred as i32 (the default)
    let y = true;      // inferred as bool

    @dbg(x);
    x
}

When there's no context, integer literals default to i32.

Booleans

Boolean values are either true or false:

fn main() -> i32 {
    let flag: bool = true;
    let done = false;

    @dbg(flag);   // prints: 1 (true)
    @dbg(done);   // prints: 0 (false)

    0
}

Integer Casts

To convert between integer types, use @intCast. The target type is inferred from context:

fn main() -> i32 {
    let big: i64 = 1000;
    let small: i32 = @intCast(big);  // i64 -> i32

    let index: i32 = 5;
    let as_u64: u64 = @intCast(index);  // i32 -> u64

    @dbg(small);   // prints: 1000
    @dbg(as_u64);  // prints: 5
    0
}

If the value doesn't fit in the target type, the program panics at runtime:

fn main() -> i32 {
    let x: i32 = 300;
    let y: u8 = @intCast(x);  // panics: 300 doesn't fit in u8 (max 255)
    @intCast(y)
}

Mutability

Variables are immutable by default. Use let mut to make them mutable:

fn main() -> i32 {
    let mut count = 0;
    count = count + 1;
    count = count + 1;
    @dbg(count);  // prints: 2
    count
}

Trying to assign to an immutable variable is a compile error:

fn main() -> i32 {
    let x = 42;
    x = 43;  // Error: cannot assign to immutable variable
    x
}

This helps catch bugs—if a value shouldn't change, the compiler enforces it.