Trait intersections
The & operator combines two or more traits into one type, in a parameter, a field or a bound, and only traits can be combined this way.
& combines several traits into one type: a value of type Show & Encode is one that implements both. It is the
same & in a parameter's type, in a field's type, and in a bound.
Example#
trait Loud {
fn shout(): String
}
trait Named {
fn name(): String
}
type Robot with Loud, Named {
robotName: String
fn shout(): String {
"BEEP"
}
fn name(): String {
robotName
}
}
fn announce(entry: Loud & Named) {
print "{entry.name()}: {entry.shout()}"
}
announce Robot("wall-e")Syntax#
fn <name>(<param>: <Trait> & <Trait>) a parameter's type
<field>: <Trait> & <Trait> a field's type
where <Item>: <Trait> & <Trait> a boundRules#
&combines two or more traits into a single type.Loud & Namedis a type any value can coerce to if its own type implements bothLoudandNamed.Only traits can be combined with
&. Two different types have no values in common, so an intersection of types would always be empty; the compiler names both offenders.Does not compilefn combine(value: Int & String) { print value } // error: `Int64` is a type, not a trait // error: `String` is a type, not a traitAn intersection is a trait type like any other, so it stands wherever a trait type does: a parameter, a field, a type argument (
List<Show & Hash>), or a bound (where Item: Compare & Show).A value coerces to an intersection only where every combined trait is implemented. A type with only one of the combined traits does not coerce, with the same
does not implementdiagnostic a single missing trait gives:Does not compiletrait Loud { fn shout(): String } trait Named { fn name(): String } type Robot with Loud { fn shout(): String { "BEEP" } } fn announce(entry: Loud & Named) { print entry.shout() } announce Robot() // error: `Robot` does not implement `Named`Object safety is checked per member, across every trait in the intersection. A member of
Namedthat mentionsSelfis still rejected on aLoud & Namedvalue, exactly as it would be rejected on a bareNamedvalue. See Object safety.
What this is not#
& is not ,. A with list at a declaration separates several traits a type provides
(type Robot with Loud, Named); & builds one type out of several traits, for a position that only takes one type.
type Robot with Loud, Named {
robotName: String
fn shout(): String {
"BEEP"
}
fn name(): String {
robotName
}
}trait Loud {
fn shout(): String
}
trait Named {
fn name(): String
}
fn announce(entry: Loud, Named) {
}
// error: The parameter `Named` needs a type
// error: A parameter starts with a lowercase letter: write `named`fn announce(entry: Loud, Named) parses as two parameters, the second one missing its name - not as one parameter
of an intersection type. Loud & Named is the only way to ask for both on one value.
Related#
- Traits as types - a single trait used as a type, before combining several.
- Bounds - the same
&restricting a type parameter. - Object safety - what stays uncallable on an intersection.