Traits & macros

Trait definitions, impl for, and macro expansion.

trait Show {
    fn show(self)
}

struct Counter {
    value: i32
}

impl Show for Counter {
    fn show(self) {
        print(self.value)
    }
}

Macros are parsed and expanded in a dedicated compiler pass before typecheck.

Static vs dynamic dispatch

Static: impl Trait for Type — method calls on a concrete struct resolve to a mangled function at compile time.

Dynamic: dyn Trait trait objects — cast with value as dyn Trait, pass to functions taking dyn Trait, call methods through a vtable. Multi-method traits use per-method vtable slots; trait objects run a Drop thunk that frees heap-boxed data.

Multi-trait objects: dyn A + B (and optional + Send / + Sync auto bounds) use a combined vtable. The concrete type must implement every listed trait; methods are dispatched in trait order (first-wins on name clashes). See examples/trait_dyn_ab.ny and examples/dyn_multi_trait.ny.

Trait bounds: fn f<T: Trait>(x: T) — generic functions constrained by traits; validated when monomorphized. See examples/trait_bounds.ny.

trait Add {
    fn add(self, other)
}

struct Counter {
    value: i32
}

impl Add for Counter {
    fn add(self, other) {
        return self.value + other
    }
}

fn via_dyn(g) {
    return g.add(1)
}

Multi-trait example:

trait Add {
    fn add(self, other)
}
trait Scale {
    fn scale(self, k)
}

struct Counter {
    value: i32
}

impl Add for Counter {
    fn add(self, other) {
        return self.value + other
    }
}
impl Scale for Counter {
    fn scale(self, k) {
        return self.value * k
    }
}

fn both(g) {
    return g.add(1) + g.scale(2)
}

fn main() {
    let c = Counter { value: 10 }
    print(both(c as dyn Add + Scale))
}

See examples/trait_dyn.ny, examples/trait_dyn_multi.ny, examples/trait_dyn_ab.ny, examples/dyn_multi_trait.ny, and tests/nyra/trait_dispatch_test.ny, trait_dyn_multi_test.ny.