Traits & Extend
trait describes capabilities a type must provide. Use traits to constrain generics and organize shared APIs. A trait is not a normal value type.
Defining Traits
Section titled “Defining Traits”Empty traits can be declared with a semicolon:
trait Numeric;Traits with required methods use a body:
trait Equatable { Bool equal(Self& lhs, Self& rhs);}
trait Hasher { () write(Self&! self, UInt8[]& bytes); UInt64 finish(self);}
trait Hashable: Equatable { () hash<H: Hasher>(self, H&! hasher);}Requirements with a self parameter use the current value as the receiver. Requirements without
self are associated functions on the implementing type.
Hashable feeds a value into any concrete Hasher; it does not choose or return a hash algorithm
itself. Hasher also provides write overloads with default implementations for primitive values.
Dynamic Trait Values
Section titled “Dynamic Trait Values”Dynamic trait values use compiler-provided companion types on every trait:
Writer.Any& writer;Writer.Receiver receiver;Any and Receiver are reserved companion type names on traits; they are not user-defined
associated types. Writer.Any& is a borrowed dynamic view of any value whose concrete type implements
Writer. Construct it with Writer$.ref(value):
Writer.Any& writer = Writer$.ref(file);The construction requires typeof(file): Writer; it is not an unchecked cast and it does not move
file. Use Writer$.new(value) when the dynamic trait value should own the receiver:
Writer.Any^ writer = Writer$.new(file);An owning dynamic trait value stores the receiver and drops it when the Writer.Any^ is dropped.
The compiler keeps a method table for the concrete implementation internally; that table type is not
a public type user source can name. Writer.Receiver is the erased receiver used by instance-method slots:
Writer.Receiver receiver = writer.receiver;writer.write(123);Writer.Any& conceptually stores a Writer.Receiver plus the matching method table. Calls through
Writer.Any& dispatch through that table:
() write_one(Writer.Any& writer) { writer.write(123);}Passing a trait method pointer uses the unbound vtable slot form. Receiver methods include the erased receiver as their first parameter, while associated functions keep their declared parameter list:
RawFn<(), Writer.Receiver, Int> write_slot = writer.write;write_slot(writer.receiver, 123);The current implementation supports dynamic dispatch for reference receiver methods, method slot access, and dropping owning dynamic trait values.
Implementing in a Type Body
Section titled “Implementing in a Type Body”Types can list traits in their declaration and provide matching methods:
trait HasValue { Int value(self);}
struct Box: HasValue { Int inner;
Int value(self) { return self.inner; }}extend
Section titled “extend”extend adds methods or a trait implementation after the type declaration:
struct User { Int id;}
extend User { Int id_value(self) { return self.id; }}
extend User: HasValue { Int value(self) { return self.id; }}extend Type: Trait { ... } provides that trait’s required methods for the type. Generic
extensions, @where type-shape matching, module visibility, and public extend are covered in
Extensions.
Associated Types
Section titled “Associated Types”trait Iterator { associated Element; Element? next(self);}Associated types can have bounds:
trait HasItem { associated Element: Hashable & Equatable; Element item(self);}Implementations bind associated types:
struct Counter: Iterator { associated Element = UInt8;
UInt8? next(self) { return null; }}extend can bind them too:
struct RangeCounter {}
extend RangeCounter: Iterator { associated Element = Int;
Int? next(self) { return null; }}Use a qualified associated type path when multiple traits expose the same associated name:
trait Left { associated Item; Item left(self);}
trait Right { associated Item; Item right(self);}
struct Pair: Left, Right { associated Left.Item = UInt8; associated Right.Item = Int;
UInt8 left(self) { return 20; }
Int right(self) { return 22; }}Trait Bounds
Section titled “Trait Bounds”@where(T: Hashable & Equatable)UInt hash_key<T>(T value);Associated type binding:
@where(T: Sequence<Element = UInt8>)Int count<T>(T value);Projected equality:
@where(T.[Sequence].Element == UInt8)Int count_bytes<T>(T value);Built-in Traits
Section titled “Built-in Traits”The prelude provides common trait and bound names such as:
NumericMutableHashableEquatableFromStringLiteralIterator/SequenceSubscriptGet/SubscriptSet
Subscript traits can model value[index]:
public trait SubscriptGet { associated Index: Equatable; associated Value;
Value subscript_get(self, Index index);}
public trait SubscriptSet: SubscriptGet { () subscript_set(self, Index index, Value value);}