Interfaces and impls
An interface declares a set of methods (and lets) a type can promise to implement; an impl is where a type actually satisfies one, or gains inherent methods with no interface involved at all.
Declaring an interface
interface Area {
func area(): int
}An interface method can carry a default body, in terms of the interface's other methods — implementors get it for free unless they override it:
interface Describable {
func label(): int
func doubled_label(): int = this.label() * 2
}Overriding a default wins over it — an implementor's own body is always what actually runs, even when the interface method it's overriding is only ever called through the default of another method:
interface MyComparable<Other> {
func compare_to(other: Other): int
func less_than(other: Other): boolean = this.compare_to(other) < 0
}
struct Weird(v: int)
impl MyComparable<Other = Weird> for Weird {
func compare_to(other: Weird): int = this.v - other.v
func less_than(other: Weird): boolean = true
}Super-interfaces
interface B: A { … } declares B as requiring A too. A type satisfying both implements each one in its own impl block — the super-interface relationship affects what a bound accepts, not how the methods are grouped at the impl site:
interface Named { func name(): string }
interface Greeter: Named { func greet(): string }
struct Robot(id: int)
impl Named for Robot {
func name(): string = "R-${this.id}"
}
impl Greeter for Robot {
func greet(): string = "Hello, ${this.name()}!"
}Generic interfaces
An interface can itself be generic, most commonly to parameterize the type on the other side of a method (an Other operand, an Output, an Item):
interface Plus<Other, Output> {
func plus(other: Other): Output
}This is exactly the shape the stdlib's ambient operator interfaces use (in fact it's almost verbatim Plus itself, already declared for you — see that page for the full list, and why this sample can't redeclare it here).
impl
Inherent impls
impl Type { … } (or the equivalent nested form inside the struct/enum body) adds methods with no interface attached:
struct Square(side: int)
impl Square {
func doubled_side(): int = this.side * 2
}Interface impls
impl Interface for Type { … } satisfies Interface for Type. Every non-defaulted method (and any defaults you want to override) goes in the body:
interface Area { func area(): int }
struct Square(side: int)
impl Area for Square {
func area(): int = this.side * this.side
}The same thing can be written nested inside the struct/enum body instead of as a separate top-level impl:
interface Area { func area(): int }
struct Circle(radius: int) {
impl Area {
func area(): int = 3 * this.radius * this.radius
}
}Generic impls
impl<T> … introduces a type parameter for the impl itself — usable both for "for every T" impls (a blanket impl) and for implementing an interface for one specific instantiation of a generic type:
struct Slot<T>(value: T, occupied: boolean)
impl<T> Slot<T> {
func get(): T = this.value
func is_free(): boolean = !this.occupied
}interface Describe { func describe(): string }
impl<T> Describe for T {
func describe(): string = "a value"
}
func demo(): string = 5.describe()When both a blanket implementation and an implementation for a specific concrete type apply, the concrete implementation takes precedence. A blanket method has one shared generic body regardless of how many concrete receiver types use it; this does not change source-level receiver behavior or argument evaluation order.
Bounds
<T: Interface> on a function, struct, enum, or method restricts T to types that implement Interface; <T: A + B> (an intersection type) requires more than one at once. Inside the bounded scope, T's interface methods are callable exactly as if T were a concrete type:
interface Area { func area(): int }
interface Named { func name(): string }
struct Square(side: int)
impl Area for Square { func area(): int = this.side * this.side }
impl Named for Square { func name(): string = "square" }
func describe<T: Area + Named>(shape: T): string = "${shape.name()}: ${shape.area()}"impl Trait as a parameter shorthand
Writing an interface name directly as a parameter's type (instead of naming a bound type parameter) is sugar for "accepts any type implementing this interface" — the parameter is used exactly like a bounded generic inside the body, and a concrete implementing type can be passed straight in from the call site:
interface Area { func area(): int }
struct Square(side: int)
impl Area for Square {
func area(): int = this.side * this.side
}
func measure(shape: Area): int = shape.area()
func total(s: Square): int = measure(s)Shared method names
The same method name can be defined independently on unrelated types — inherently on one, through different interfaces on others — and each call resolves against its own receiver's type with no ambiguity between them:
interface Scored { func score(): int }
struct Player(points: int)
impl Scored for Player { func score(): int = this.points }
struct Judge(bias: int) {
func score(): int = this.bias
}
func tally(p: Player, j: Judge): int = p.score() + j.score()