lang::trait¶
Behaviour interface declaration.
A trait declares method signatures; a type provides them with impl Trait
for Type. A generic function bounds a parameter by a trait and calls its
methods (fn report<T: Shape>(s: &T)) - see generics.
trait Area { fn area(&self) -> f64 }
impl Area for Shape {
fn area(&self) -> f64 {
match self {
Shape::Circle(r) => 3.14159 * r * r,
Shape::Rect { w, h } => w * h,
}
}
}
Associated types¶
A trait declares type Item and each impl supplies one concrete type
for it. Self::Item and T::Item name that type in signatures and
bodies; the projection resolves before lowering, so every tier sees the
concrete type.
trait Holder {
type Item
fn get(&self) -> Self::Item
}
struct Label { text: String }
impl Holder for Label {
type Item = String
fn get(&self) -> Self::Item { self.text }
}
fn shout<T: Holder>(holder: &T) -> T::Item { holder.get() }
A trait may give the associated type a default (type Count = i64),
which an impl inherits unless it restates it.
When several impls supply different types, pin the projection with an equality constraint on the bound:
Resolution order is: the equality constraint, the impl named by a
concrete base (Label::Item, or Self::Item inside an impl), the
trait's default, then the trait's single implementor. A supertrait's
associated items are reachable through the subtrait that inherits them.
An impl that omits a required associated item is rejected (GT0059), a
projection of an undeclared item is rejected (GT0060), and an ambiguous
projection is rejected with the constraint to write (GT0061).
Out of scope: generic associated types (type Item<T>), associated types
on dyn Trait (Gossamer has no trait objects), and inferring a
projection across several candidate impls without a constraint.
Associated constants¶
A trait declares const MAX: i64, optionally with a default; each impl
supplies a value. Read one as Type::MAX, Self::MAX, or T::MAX
through a bound.
trait Bounded {
const MAX: i64
const STEP: i64 = 5
fn width(&self) -> i64
}
struct Gauge { span: i64 }
impl Bounded for Gauge {
const MAX: i64 = 100
fn width(&self) -> i64 { self.span + Self::MAX }
}
fn headroom<T: Bounded>(gauge: &T) -> i64 { T::MAX - gauge.width() + T::STEP }
Each associated constant compiles to an ordinary constant, so its value
folds identically on the bytecode VM, the JIT, and the LLVM AOT tier.
T::MAX follows the same resolution order as a type projection: the
trait's default, else the trait's single implementor.
Operator overloading¶
Implementing the matching trait makes an operator dispatch to its method
on a user struct, enum, or generic struct - on every tier (bytecode VM,
JIT, and LLVM AOT alike). The result type is the method's return type, so
a dot product (Mul -> f64) types correctly. Compound assignment
(a += b) routes through the same binary method.
| Operator | Trait | Method |
|---|---|---|
a + b / a - b / a * b / a / b |
Add / Sub / Mul / Div |
add / sub / mul / div |
a % b |
Rem |
rem |
-a (unary) |
Neg |
neg |
a[i] |
Index |
index |
a | b / a & b / a ^ b |
BitOr / BitAnd / BitXor |
bitor / bitand / bitxor |
a << b / a >> b |
Shl / Shr |
shl / shr |
struct V2 { x: f64, y: f64 }
impl Add for V2 { fn add(self, o: V2) -> V2 { V2 { x: self.x + o.x, y: self.y + o.y } } }
impl Mul for V2 { fn mul(self, o: V2) -> f64 { self.x * o.x + self.y * o.y } }
Applying an arithmetic operator to an ADT with no matching impl is a
compile error (GT0003). These are real impls, not derives - the operator
traits are not #[derive]-able.
Conversions: From / TryFrom¶
A from impl powers both B::from(x) and x.into(); a try_from impl
powers B::try_from(x) and x.try_into() -> Result<B, E>. The into /
try_into target is inferred from the use site (let B, a B parameter
or return) and never from the receiver, so a bare x.into() that no use
site reaches has no target at all and is reported as GT0066:
struct Celsius { t: i64 }
struct Fahrenheit { t: i64 }
impl Fahrenheit {
fn from(c: Celsius) -> Fahrenheit { Fahrenheit { t: c.t * 9 / 5 + 32 } }
}
let f: Fahrenheit = Celsius { t: 100 }.into() // 212, via Fahrenheit::from
? also auto-converts a propagated Err through errors::Error::from,
so a Result<_, String> flows into a Result<_, errors::Error> function
with no explicit map_err.