Traits
Named interfaces with default method implementations, supertrait requirements, associated types, and static checking.
Summary
A trait declares method signatures; types implement them with impl TraitName for TypeName. Default implementations can be overridden. Supertraits require another trait to be implemented first. Associated types let a trait declare type names that implementing types define. The semantic analyzer enforces the orphan rule (no implementing foreign traits for foreign types), missing required methods, and signature mismatches.
Canonical
Traits define shared behavior across types.
trait Describe {
fn describe(self) -> str
}
struct Dog { name, breed }
struct Car { make, year }
impl Describe for Dog {
fn describe(self) -> str {
return "{self.name} the {self.breed}"
}
}
impl Describe for Car {
fn describe(self) -> str {
return "{self.year} {self.make}"
}
}
let d = Dog { name: "Rex", breed: "Shepherd" }
println(d.describe()) -- Rex the Shepherd
let car = Car { make: "Volvo", year: 2024 }
println(car.describe()) -- 2024 VolvoA type can implement multiple traits:
trait Area {
fn area(self) -> f64
}
struct Circle { radius }
impl Describe for Circle {
fn describe(self) -> str { return "circle r={self.radius}" }
}
impl Area for Circle {
fn area(self) -> f64 { return 3.14159 * self.radius * self.radius }
}Default Methods
Traits can provide default implementations. Types only need to override them if they want different behavior.
trait Greet {
fn hello(self) -> str {
return "hello from {self.name}"
}
fn goodbye(self) -> str -- no default, must implement
}
struct Person { name }
impl Greet for Person {
-- hello() uses the default, only goodbye() is required
fn goodbye(self) -> str { return "bye from {self.name}" }
}Super Traits
A trait can require another trait as a prerequisite:
trait Display {
fn display(self) -> str
}
trait PrettyPrint: Display {
fn pretty(self) -> str
}
-- implementing PrettyPrint for a type requires Display to also be implementedAssociated Types
Traits can declare associated type names:
trait Iterator {
type Item
fn next(self) -> Item
}Trait Checking
The semantic analyzer enforces trait implementations:
- Missing methods: if an impl block is missing a required method (one without a default body), that's an error.
- Parameter count mismatch: if the impl's method has a different number of params than the trait declares, that's an error.
- Return type mismatch: if the trait declares
-> strand the impl returns-> int, that's an error. - Orphan rule: you can only implement a trait if either the trait or the type is defined in the same file. Implementing a foreign trait for a foreign type is an error.