Structures

Structures

Structs are plain data types no constructors, no methods in the body, trivially copyable via memcpy. They exist for cases where you want a named bag of fields with aggregate initialization and no lifecycle management. If you need constructors, destructors, inheritance, or virtual dispatch, use a class.


Declaration

struct Point {
    var x: f64
    var y: f64
}

var p = Point { x: 1.0, y: 2.0 }
p.x   // 1.0

The body contains only field declarations (var, const, eval, static) and nested type definitions. Methods and constructors are not permitted inside the struct body use extends to add behavior.


Aggregate Initialization

Structs are constructed with brace-delimited field assignment. All fields must be provided unless they have a default value at the declaration site:

struct Color {
    var r: u8
    var g: u8
    var b: u8
    var a: u8 = 255
}

var red = Color { r: 255, g: 0, b: 0 }         // a defaults to 255
var blue = Color { r: 0, g: 0, b: 255, a: 128 } // explicit alpha

Field names are required in the initializer positional initialization is not supported. The order of fields in the initializer does not need to match the declaration order.

There are no user-defined constructors. If you need construction logic, extend a factory function onto the struct:

extend Color {
    static fn from_hex(hex: u32) -> Color {
        return Color {
            r: (hex >> 16 & 0xFF) as u8,
            g: (hex >> 8 & 0xFF) as u8,
            b: (hex & 0xFF) as u8,
        }
    }
}

var teal = Color::from_hex(0x008080)

Visibility

All struct members default to pub. You can explicitly mark a member priv or prot, but the compiler emits a warning if you need access control on fields, a class is the better fit.

struct Packet {
    var header: u32        // pub by default
    priv var checksum: u32 // legal but warned: consider using a class
}

Extended functions can be pub, prot, or priv without warning.


const and mutable Members

const members must be initialized at the declaration site or in the aggregate initializer. Unlike class const members, there is no constructor body to provide a one-shot assignment:

struct Config {
    const VERSION: i32 = 1
    var name: string
}

var cfg = Config { name: "app" }
// cfg.VERSION = 2   // compile error: VERSION is const

mutable works the same as in classes the member can be modified even through a const reference:

struct Metrics {
    var total: i32
    mutable var cache_hits: i32
}

fn report(const self: Metrics) {
    self.cache_hits += 1   // ok: mutable
    // self.total += 1     // compile error: total is not mutable, self is const
}

See Classes for the mutable rationale and usage guidance.


Copy Semantics

Structs are always trivially copyable. Assignment and parameter passing use memcpy there are no copy constructors, move constructors, or assignment operator overloads:

var a = Point { x: 1.0, y: 2.0 }
var b = a   // memcpy, a and b are independent copies
b.x = 9.0
a.x   // still 1.0

This is a hard guarantee. Extending a destructor (fn op delete), copy assignment (fn op =), or move assignment onto a struct is a compile error. If you need custom lifecycle management, use a class.


Extends

Since structs cannot contain methods in their body, behavior is added via extend blocks. An extend block can add methods, operators, and static functions:

struct Vec2 {
    var x: f64
    var y: f64
}

extend Vec2 {
    fn length(self) const -> f64 {
        return std::sqrt(self.x * self.x + self.y * self.y)
    }

    fn op +(self, other: Vec2) -> Vec2 {
        return Vec2 { x: self.x + other.x, y: self.y + other.y }
    }

    fn op ==(self, other: Vec2) const -> bool {
        return self.x == other.x && self.y == other.y
    }
}

var v = Vec2 { x: 3.0, y: 4.0 }
v.length()   // 5.0

What extends can add

AllowedNot allowed
MethodsConstructors
Static functionsDestructors (fn op delete)
Arithmetic / comparison operatorsCopy / move assignment (fn op =)
fn op as (type conversion)
fn op in (iteration)

Interface conformance

Structs implement interfaces through extend ... impl:

interface Drawable {
    fn draw(self) -> string
}

extend Vec2 impl Drawable {
    fn draw(self) -> string {
        return f"({self.x}, {self.y})"
    }
}

The extend block and the struct definition must be in the same file the same restriction as Rust’s impl blocks. See Extends for the full extend system and Interfaces for interface declarations.


Generic Structs

Type parameters are declared in angle brackets before the struct name:

struct <T> Pair {
    var first: T
    var second: T
}

var p = Pair<i32> { first: 10, second: 20 }

Generic extends must redeclare the type parameters:

extend <T> Pair<T> {
    fn swap(self) -> Pair<T> {
        return Pair<T> { first: self.second, second: self.first }
    }
}

Constrain type parameters with impl or derives bounds:

struct <T impl Comparable> Range {
    var start: T
    var end: T
}

See Bounds for the full constraint system.


Nested Types

Structs can contain nested type definitions classes, enums, unions, other structs:

struct Packet {
    var header: Header
    var payload: [byte]

    struct Header {
        var version: u8
        var length: u16
    }
}

var pkt = Packet {
    header: Packet::Header { version: 1, length: 64 },
    payload: [],
}

Nested types are accessed via StructName::NestedType. Interfaces cannot be nested they must be declared at the top level.


No Inheritance

Structs do not support derives. If you need field inheritance, embed the struct:

struct Base {
    var x: i32
}

struct Composed {
    var base: Base
    var y: i32
}

var c = Composed { base: Base { x: 10 }, y: 20 }
c.base.x   // 10

This keeps aggregate initialization unambiguous and avoids layout questions that inheritance introduces. If you need polymorphism or a type hierarchy, use classes.


Destructuring

Struct fields can be destructured into individual bindings:

struct Color {
    var r: u8
    var g: u8
    var b: u8
}

var color = Color { r: 255, g: 128, b: 0 }
var { r, g, b } = color
// r = 255, g = 128, b = 0

Use _ to discard fields you do not need. See Variables for the full destructuring syntax.


Memory Layout

Struct layout follows the platform’s C++ ABI members in declaration order with standard padding and alignment rules. Control layout with attributes:

@packed
struct Tight {
    var a: u8    // offset 0
    var b: u32   // offset 1 (no padding)
}

@align(16)
struct Aligned {
    var data: [u8; 12]
}

Structs are always value types. A plain var declaration allocates on the stack. Heap allocation uses std::create<T>():

var local = Point { x: 1.0, y: 2.0 }           // stack
var *heap = std::create<Point>(Point { x: 1.0, y: 2.0 })  // heap

See Pointers and AMT for allocation and pointer semantics.


Forward Declarations

Structs can be forward-declared for use in pointer types before the full definition is available:

struct Node    // forward declaration

struct Tree {
    var root: unsafe *Node
}

struct Node {
    var value: i32
    var left: unsafe *Node
    var right: unsafe *Node
}

Structs vs Classes

StructClass
Member default visibilitypubpriv for variables
Methods in bodyNo (use extends)Yes
ConstructorsNo (aggregate init only)Yes
DestructorsNoYes
Copy semanticsmemcpy (trivial)Rule of five
InheritanceNoderives
Virtual dispatchNoYes
Aggregate initializationYesNo

Use structs for plain data configuration records, protocol headers, math vectors, tuple-like types. Use classes when you need lifecycle control, inheritance, or encapsulation.


Summary

// Basic struct
struct Point {
    var x: f64
    var y: f64
}

var p = Point { x: 1.0, y: 2.0 }

// Generic struct
struct <T> Pair {
    var first: T
    var second: T
}

// Extend with methods and operators
extend Point {
    fn length(self) const -> f64 {
        return std::sqrt(self.x * self.x + self.y * self.y)
    }

    fn op +(self, other: Point) -> Point {
        return Point { x: self.x + other.x, y: self.y + other.y }
    }
}

// Extend with interface conformance
extend Point impl Drawable {
    fn draw(self) -> string {
        return f"({self.x}, {self.y})"
    }
}

// Layout control
@packed
struct Header {
    var magic: u16
    var version: u8
    var flags: u8
}

// Composition over inheritance
struct Rect {
    var origin: Point
    var size: Point
}

var r = Rect {
    origin: Point { x: 0.0, y: 0.0 },
    size: Point { x: 100.0, y: 50.0 },
}