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 written | The implementation receives | Callable 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
extendline: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) -> int32for the requirementfunc Area() -> float64;fails withmethod '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 Tfails withmethod '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 Selfrequirement may takeself: &Tif it does not need to write. - The receiver is a reference.
self: Tby value is refused in a block that implements an interface. - One interface per block.
extend Circle : Shape, Nameddoes not parse; a type that implements several interfaces has oneextendblock 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
| Written | What it is | Page |
|---|---|---|
let shape: Shape = wheel; | an interface value holding a copy | Interface values |
func Show(shape: &Shape) | a borrow of any implementing value | Interface values |
func Best<T: Shape>(a: T, b: T) | a generic accepting implementing types | Bounds |
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
- Interface values — holding and borrowing values through an interface
- Core interfaces —
Equatable,Comparable,Hashable,Displayand the rest - Bounds — interfaces as requirements on type parameters
- Extensions — the
extendblock - Learn: Interface, Interface parameter