Impl Blocks (Methods)
An impl block attaches methods and associated functions to a struct.
impl Person {
// Associated function (static)
fn new(name: str, age: int) -> Person {
return Person { name: name, age: age }
}
// Instance method
fn greet() -> str {
return "Hello, ${self.name}!"
}
}Instance Methods
Inside methods, the special identifier self always refers to the current instance.
- You do not declare it
- You do not list it in the parameter list
- It is implicitly available inside any method in
impl
fn greet() -> str {
return "Hello, ${self.name}!"
}Mutating self
Methods may freely mutate fields:
fn birthday() {
self.age += 1
}self acts as a mutable reference to the instance.
Calling methods
bob: Person = Person.new("Bob", 30)
print(bob.greet())Method Dispatch Rules (Important)
Luma uses simple, predictable rules:
Instance type → uses inherent methods (impl Struct)
alice: Person
alice.greet() // uses Person’s own greet() methodTrait type → uses trait implementation (impl Trait for Struct)
person: Greetable = alice
person.greet() // uses Greetable’s greet() implementationThis is the same model used by Go interfaces and Rust traits.
Full Example
struct Vector2 {
x: float,
y: float
}
trait Movable {
fn move(self, dx: float, dy: float)
}
impl Movable for Vector2 {
fn move(self, dx: float, dy: float) {
self.x += dx
self.y += dy
}
}
// Usage
pos: Vector2 = Vector2 { x: 0.0, y: 0.0 }
pos.move(1.5, 2.0)
print(pos) // Vector2 { x: 1.5, y: 2.0 }Why Structs & Traits (Not Classes)?
Luma avoids classes intentionally:
- No inheritance chains
- No hidden magic
- No subclass surprises
- Behavior is granular and composable
- Data stays separate from logic
- More consistent with functions, lambdas, and traits
This gives clearer programs and fewer surprises for new developers.
Last updated on