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 Volvo

A 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 implemented

Associated 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 -> str and 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.