Interfaces

An interface names a set of functions that a type must provide. It has no fields and no code of its own: it lists function headers, and each type that implements it supplies the bodies in an extend block. Once a type implements an interface, its values can be held, passed and stored as interface values with dynamic dispatch, and generic code can require the interface as a bound.

Declaring an interface

interface Name {
    func Requirement(parameters) -> Result;
    func Requirement(self: &Self, parameters) -> Result;
    func Requirement(self: &var Self, parameters) -> Result;
}
interface Shape {
    func Area() -> float64;
    func Name() -> char8[..];
}

Each requirement is a function header ending in ;. An interface may declare any number of requirements, including none, and is private to its module unless declared pub interface.

A requirement says how it reaches the implementing value:

Requirement writtenThe implementation receivesCallable through
func Area() -> float64; (no receiver)self: &T — it reads&I, &var I, I
func Peek(self: &Self) -> int32;self: &T — it reads&I, &var I, I
func Turn(self: &var Self, by: int32);self: &var T — it may write&var I, a var I

A receiver taken by value is refused, because an interface reaches the implementing value through a reference:

error: receiver of requirement 'Consume' in interface 'Sink' must be '&Self' or '&var Self'
  note: a requirement reaches the implementing value through the data half of an interface view
  help: write 'self: &var Self' if 'Consume' writes through its receiver, or 'self: &Self' if it only reads it

Self

Inside an interface, Self names the type that implements it. It is how an interface says that an operand or a result has the implementer's own type — Core::Equatable declares func Equals(other: &Self) -> bool;, so a Fraction is compared only with another Fraction. Self exists only inside an interface; anywhere else it is type 'Self' is not defined in this scope.

No type parameters

An interface takes no type parameters. interface Box<T> { … } does not parse:

error: expected '{' to start the interface body before '<'

A requirement that would need one is written with Self, or the generic part moves to the functions that use the interface, as bounds. This is also why Core::Iterator lists no requirements: an iterator's item type differs per implementation, and an interface has no way to name it.

No bodies

A requirement has no body and an interface supplies no default implementations: every implementing type writes every requirement. A requirement written with a body, such as func Area() -> float64 { return 1.0; }, is refused:

error: requirement 'Area' of interface 'Shape' cannot have a body
  help: end the requirement with ';', and write the body in each 'extend Type : Shape'

Implementing an interface

extend Type : Interface { methods }
extend Interface for Type { methods }

The two spellings are equivalent. Inside the block, every requirement appears as an ordinary method, with the receiver of the concrete type in front of the requirement's own parameters:

struct Circle {
    radius: float64;
}

extend Circle : Shape {
    func Area(self: &Circle) -> float64 {
        return 3.14 * self.radius * self.radius;
    }

    func Name(self: &Circle) -> char8[..] {
        return "circle";
    }
}

struct Square {
    side: float64;
}

extend Shape for Square {
    func Area(self: &Square) -> float64 {
        return self.side * self.side;
    }

    func Name(self: &Square) -> char8[..] {
        return "square";
    }
}

The rules:

  • Every requirement must be present. A missing one is reported on the extend line: implementation of interface 'Shape' for type 'Circle' is missing method 'Name'.
  • The parameters and the result must match the requirement exactly. Only the receiver is added. A method func Area(self: &Circle) -> int32 for the requirement func Area() -> float64; fails with method 'Area' does not match the requirement of interface 'Shape', and notes the two headers side by side.
  • The receiver must not write more than the requirement allows. An implementation of a reading requirement that takes self: &var T fails with method 'Adjust' of 'Dial' writes through its receiver, but requirement 'Adjust' of interface 'Gauge' only reads it. The opposite is allowed: an implementation of a &var Self requirement may take self: &T if it does not need to write.
  • The receiver is a reference. self: T by value is refused in a block that implements an interface.
  • One interface per block. extend Circle : Shape, Named does not parse; a type that implements several interfaces has one extend block for each.
  • The block may add methods that the interface does not require. They belong to the type, not to the interface.

Methods that implement an interface are ordinary methods of the type, and are called on it like any other: wheel.Area().

Which types can implement

Any type with a declaring package can implement an interface: a structure, an enum, a variant, a primitive such as int32, a slice type such as char8[..], and a generic type through extend Box<T> : Shape (see Generic types). A native form — an optional, a sum, a fallible or the unit type — has no declaring package to own the implementation:

error: cannot extend native type 'int32?'
  note: a sum, optional, fallible, or unit type has no declaring package to own methods or interface implementations
  help: write a generic function that takes the native type as a parameter

An interface from another package is imported before it is implemented: import Core::Equatable;, then extend Fraction : Equatable { … }.

Declared, not inferred

Implementing an interface is a declaration. A plain extend Circle { … } that happens to contain the right methods does not make Circle a Shape value: let shape: Shape = wheel; then fails with cannot assign 'Circle' to 'Shape'. Generic bounds are the exception — they are satisfied by any type that has the required methods.

Using an interface

WrittenWhat it isPage
let shape: Shape = wheel;an interface value holding a copyInterface values
func Show(shape: &Shape)a borrow of any implementing valueInterface values
func Best<T: Shape>(a: T, b: T)a generic accepting implementing typesBounds

The standard packages define a handful of interfaces the language and libraries build on — equality, ordering, hashing, text output and iteration; see Core interfaces. Operators, indexing and for loops are not interfaces: a type provides them by declaring operator functions, indexers and the iteration methods.

See also