Structs & Enums

Define custom data types with structs and algebraic data types with enums.

Struct Definitions

struct Point {
    x: i64,
    y: i64,
}

fn main() {
    let p = Point { x: 3, y: 4 }
    print(p.x)    // 3
    print(p.y)    // 4
}

Mutable Struct Fields

fn main() {
    let mut p = Point { x: 0, y: 0 }
    p.x = 10
    p.y = 20
    print(p.x)    // 10
}

Impl Blocks and Methods

Attach methods to structs with impl blocks:

struct Rectangle {
    width: f64,
    height: f64,
}

impl Rectangle {
    fn area(self) -> f64 {
        self.width * self.height
    }

    fn is_square(self) -> bool {
        self.width == self.height
    }
}

fn main() {
    let r = Rectangle { width: 10.0, height: 5.0 }
    print(r.area())         // 50.0
    print(r.is_square())    // false
}

Generic Structs

struct Point<T> {
    x: T,
    y: T,
}

fn main() {
    let int_point = Point { x: 1, y: 2 }
    let float_point = Point { x: 1.5, y: 2.5 }
}

Derive Attributes

Auto-generate trait implementations with @derive:

@derive(Eq, Clone, Display)
struct Point { x: i64, y: i64 }

fn main() {
    let a = Point { x: 1, y: 2 }
    let b = Point { x: 1, y: 2 }
    if a == b { print("equal!") }     // @derive(Eq)
    let c = clone(a)                   // @derive(Clone)
    print(a)                           // @derive(Display) -> "Point { x: 1, y: 2 }"
}

Enum Definitions

Enums define types with a fixed set of variants. Use the type keyword:

type Color {
    Red,
    Green,
    Blue,
}

fn describe(c: Color) -> str {
    match c {
        Red => "red"
        Green => "green"
        Blue => "blue"
    }
}

fn main() {
    let c = Color.Green
    print(describe(c))    // green
}

Data-Carrying Variants

Enum variants can carry data, making them algebraic data types:

type Shape {
    Circle(f64)
    Rectangle(f64, f64)
}

fn describe(s: Shape) -> str {
    match s {
        Circle(r) => "circle with radius"
        Rectangle(w, h) => "rectangle"
    }
}

type Option<T> {
    Some(T)
    None
}

Error Handling

Turbo uses Result types T ! E for recoverable errors and Optional types T? for absent values:

// Result type: success or error
fn divide(a: f64, b: f64) -> f64 ! str {
    if b == 0.0 {
        return err("division by zero")
    }
    a / b
}

// Propagate errors with ?
fn safe_math(x: f64, y: f64) -> f64 ! str {
    let result = divide(x, y)?
    result * 2.0
}

// Optional: provide a default with ??
fn find_user(id: i64) -> str? {
    // returns None if not found
}

fn main() {
    let name = find_user(42) ?? "anonymous"
}