Variables, Types & Control Flow

Ube adopts Go's human-first clarity while enforcing strict immutability defaults, zero hidden conversions, and deterministic value scoping.

1. Variables & Immutability

In Ube, variables are bound using the let keyword. By default, every binding is strictly immutable. If you intend to reassign a variable or modify its value in place, you must explicitly mark it with mut:

fn main() {
    let name: string = "Ube"     // Immutable binding
    let speed = 60               // Type inferred as i32

    // speed = 80                // COMPILE ERROR: cannot reassign immutable binding

    let mut score = 100          // Mutable binding
    score += 25
    score *= 2
    println(f"{name} final score: {score}") // prints: Ube final score: 250
}
💡 Design Tip
Always default to let. Add mut only when in-place modification or loop accumulation is truly necessary. This helps LLMs and human reviewers trace data mutations without ambiguity.

2. Primitive Types

Ube does not have implicit type promotions (e.g., converting an i8 to an i32 automatically). All types are explicit and sized:

Type Description Literals / Example
i8, i16, i32, i64 Signed integers (8, 16, 32, 64 bits) let x: i32 = -42, 0x2A, 0b101010
u8, u16, u32, u64 Unsigned integers (8, 16, 32, 64 bits) let byte: u8 = 255
f32, f64 IEEE-754 floating point numbers let pi: f32 = 3.14159
bool Boolean truth values true or false
string UTF-8 byte sequence (arena allocated) "sweet ube"
void Return type for functions that produce no value fn cleanup() -> void { }

3. Strings & Format Interpolation

Plain string literals in Ube (written with double quotes "...") are strictly literal. If you want string interpolation, prefix the literal with f and embed expressions in {expr}:

fn main() {
    let hero = "Grounded Player"
    let hp = 100

    // Plain string (no interpolation happens)
    println("Status: {hero} has {hp} HP") 
    // Output: Status: {hero} has {hp} HP

    // Format string (interpolation enabled with 'f')
    println(f"Status: {hero} has {hp} HP, boosted: {hp + 50}")
    // Output: Status: Grounded Player has 100 HP, boosted: 150

    // Byte indexing & slicing
    let title = "UbeLanguage"
    let first_byte: u8 = title[0] // 85 ('U')
    let prefix = title[0:3]       // "Ube" (end index exclusive)
    println(f"Prefix: {prefix}, length: {title.len}")
}

4. Arrays vs Slices

Ube clearly separates fixed-size stack arrays from dynamic arena-backed slices:

fn main() {
    // Fixed array of 3 elements on the stack
    let coords: [3]f32 = [3]f32{0.0, 10.5, 20.0}
    println(coords[1]) // 10.5

    // Dynamic slice allocated in the current arena
    let mut items = []i32{}
    items.push(10)
    items.push(20)
    items.push(30)

    println(f"Items length: {items.len}") // 3
    let last = items.pop()
    println(f"Popped: {last}")            // 30
    println(f"Remaining: {items.len}")    // 2
}

5. Loops & Conditional Branches

Ube supports if / else, range-based for loops, collection iteration, and while loops:

fn main() {
    let health = 45

    if health > 50 {
        println("Status: Healthy")
    } else if health > 20 {
        println("Status: Wounded")
    } else {
        println("Status: Critical")
    }

    // Range loop (end index exclusive)
    println("Counting from 0 to 4:")
    for i in 0..5 {
        println(i) // 0, 1, 2, 3, 4
    }

    // Iterating dynamic slice
    let numbers = []i32{100, 200, 300}
    for n in numbers {
        println(f"Number: {n}")
    }

    // While loop with break & continue
    let mut step = 0
    while step < 10 {
        step += 1
        if step == 2 { continue }
        if step == 5 { break }
        println(f"Step {step}")
    }
}

6. Scope Deferral with defer

Similar to Go, Ube provides the defer keyword to schedule a cleanup call to execute whenever the enclosing function or scope exits:

fn teardown() {
    println("Cleaning up resources...")
}

fn main() {
    defer teardown()
    println("Beginning game session...")
    println("Executing gameplay logic...")
}
// Console output:
// Beginning game session...
// Executing gameplay logic...
// Cleaning up resources...