Skip to content

Traits

Kōdo supports traits for defining shared behavior across types. Traits enable static dispatch — the compiler resolves which implementation to call at compile time, not runtime.

A trait declares a set of functions that implementors must provide:

trait Printable {
fn to_display(self) -> String
}

Use impl blocks to provide trait implementations for a type:

struct Point {
x: Int,
y: Int
}
impl Printable for Point {
fn to_display(self) -> String {
return "Point"
}
}

Trait methods are called on values of types that implement the trait:

fn main() {
let p: Point = Point { x: 10, y: 20 }
println(p.to_display())
}

Kōdo uses static dispatch exclusively — the compiler knows exactly which function to call at every call site. This means:

  • Zero runtime overhead for trait calls
  • No vtable allocation or indirection
  • The compiler can inline trait method calls

When the concrete type is not known at compile time, you can use dyn Trait to dispatch method calls through a vtable at runtime.

Use dyn Trait as a parameter type to accept any value that implements the trait:

fn print_it(item: dyn Printable) {
println(item.to_display())
}

You can pass any type that implements Printable:

fn main() {
let p: Point = Point { x: 1, y: 2 }
print_it(p)
}

A dyn Trait value is represented internally as a fat pointer with two fields:

  • data_ptr — pointer to the concrete value
  • vtable_ptr — pointer to a table of function pointers for the trait’s methods

The compiler generates a vtable for each (Type, Trait) pair used with dyn. When a method is called, the runtime looks up the function pointer in the vtable and performs an indirect call.

Dynamic dispatch introduces a small overhead compared to static dispatch:

  • Each method call goes through an indirect function pointer (one extra pointer dereference)
  • The compiler cannot inline dynamically dispatched calls
  • Each dyn Trait value is two pointers wide instead of one
Use static dispatch when…Use dyn Trait when…
The concrete type is known at compile timeYou need to store heterogeneous types in a collection
Performance is criticalYou need to abstract over many types without generics
You want the compiler to inline callsThe set of types is determined at runtime

See examples/traits.ko for a complete working example.