#Traits
Status: language specification draft.
This chapter defines traits, their implementations, and how trait methods are selected.
- trait.kind.sharedTraits describe behavior shared by otherwise unrelated types.
- trait.kind.explicitConformance is explicit.
- trait.kind.no-structuralhd-lang does not use structural method matching or class inheritance.
trait Greet:
fn greet(self) -> string
struct User:
name: string
impl Greet for User:
fn greet(self) -> string:
"hi, ${self.name}"#Trait Declarations
A trait declares required methods:
trait Describe:
fn describe(self) -> stringA method with a body is a default implementation:
trait Named:
fn name(self) -> string
fn label(self) -> string:
"name: " + self.name()#Trait Members
- trait.decl.requiredA trait declares required methods.
- trait.decl.defaultA method with a body is a default implementation.
- trait.decl.uniqueMember names must be unique within a trait. A repeated associated type, method, or associated function name is an error, in a trait or in an implementation of one. Error:
duplicate-trait-member. - trait.decl.typedEvery parameter and result type is explicit; omitting one is an error. Error:
syntax-error. - trait.decl.method-receiverA function whose first parameter is a receiver,
self,mut self,&self, or&mut self, is a method. - trait.decl.mut-receiverA mutable receiver is a requirement callers must satisfy.
- trait.decl.associated-fnA function without a receiver is an associated function. It is called with qualified
Trait::function(...)orType::function(...)syntax.
trait Named:
fn name(self) -> string
fn name(self) -> string # error: duplicate-trait-member#Default Method Bodies
- trait.default.checked-onceA default method body is checked once, in the trait declaration, with
Selfbounded by the trait. - trait.default.visibleThrough
selfandSelf, the body sees only the members of the trait and of its transitive supertraits. - trait.default.no-self-membersThe fields and inherent methods of an implementing type are not accessible there, even when every implementing type declares them. Naming one there is an error. Error:
unknown-method. - trait.default.no-self-members.errorA call there of a method that neither the trait nor a supertrait declares is an error. Error:
unknown-method. - trait.default.not-per-implA default body is not checked again for an implementation, so a bound that an implementation's method adds does not make a call in the default fail, as in
annot.walk.rest.default.
trait Greeter:
fn name(self) -> string
fn greet(self) -> string:
"hi " + self.name()
fn shout(self) -> string:
self.loud_name() # error: unknown-method
fn tag(self) -> string:
self.email # error: unknown-methodSee also: Supertraits.
#Generic Traits
Traits may be generic:
trait Add[T]:
fn add(self, other: T) -> T- trait.decl.genericTraits may be generic.
- trait.decl.generic.invariantA trait's generic parameters are invariant, so
Source[*mut User]andSource[*User]are unrelated trait instantiations. Using one where the other is required is an error. Error:type-mismatch. - trait.decl.generic.no-varianceA variance marker on a trait's generic parameter is an error. Error:
invalid-variance. - trait.decl.generic.defaultA trait's generic parameter may declare a default, which may name
Self, as intrait Same[Other = Self].Selfthere is the implementing type or the bounded type.
trait Source[+T]: # error: invalid-variance
fn source(self) -> TSee also: Variance.
#Marker Traits
A trait with no methods is a marker, written without a body:
trait Serializable
impl Serializable for User- trait.marker.declA trait with no methods may be declared as a marker without a body.
- trait.marker.implThe marker's implementation likewise has no body.
- trait.marker.all-defaultsA bodyless implementation is also permitted when every method of the trait has a default.
See also: Embedding And Trait Satisfaction.
#Supertraits
A trait may require another trait using a supertrait bound:
trait Formattable < Display:
fn format(self) -> string- trait.super.boundA trait may require another trait using a supertrait bound.
- trait.super.satisfyAn implementation of
Formattablemust also satisfyDisplay. - trait.super.missingAn
impl Child for Xfor whichXhas no implementation of a supertrait ofChildis an error. Error:missing-supertrait-implementation. - trait.super.impl-boundsThe check is made at the implementation, under its own bounds: they must prove every supertrait for its target. With
impl[T < Eq] Parent for Box[T], animpl[T] Child for Box[T]is an error. Error:missing-supertrait-implementation. - trait.super.acyclicThe supertrait graph must be acyclic: a direct or indirect cycle is a compile-time error. Error:
supertrait-cycle. - trait.super.cycle-reportAn indirect cycle is reported once, on the member of the cycle that appears first. Members are ordered first by module identity, then by source position within the module.
- trait.super.uniqueA supertrait bound must not list one trait twice with the same arguments;
Convert[i32] & Convert[string]lists two bounds. Error:duplicate-supertrait.
trait Parent
trait Child < Parent:
fn value(self) -> i32
struct Item: pass
impl Child for Item: # error: missing-supertrait-implementation
fn value(self) -> i32: 42
struct Box[T]:
value: T
impl[T < Eq] Parent for Box[T]
impl[T] Child for Box[T]: # error: missing-supertrait-implementation
fn value(self) -> i32: 0
trait Loop < Loop: # error: supertrait-cycle
fn step(self) -> void
trait Twice < Parent & Parent # error: duplicate-supertrait#Supertrait Member Names
- trait.super.no-redeclareA child trait must not declare a member whose name is also the name of a member of any of its transitive supertraits. Error:
duplicate-trait-member. - trait.super.no-redeclare.whenThe check is made at the child trait's declaration, whether or not any type implements it.
- trait.super.no-redeclare.kindsThe check applies to associated types, methods, and associated functions alike.
- trait.super.no-redeclare.reportThe
duplicate-trait-membererror is reported on the child's member. - trait.super.no-redeclare.exampleFor example, when
Greeterdeclaresfn greet(self) -> string,trait Loud < Greetermust not declaregreet, with or without a body. - trait.super.no-default-overrideA child trait therefore cannot provide a default body for a supertrait's method.
- trait.super.defaultsA supertrait's defaults come only from the supertrait.
trait Greeter:
fn greet(self) -> string
trait Loud < Greeter:
fn greet(self) -> string # error: duplicate-trait-member#Associated Types
Traits may declare associated types, and implementations bind them:
trait Supplier:
type Item
fn get(&mut self) -> Self::Item?
impl Supplier for NameSupplier:
type Item = string
fn get(&mut self) -> string?:
...- trait.assoc.declareTraits may declare associated types, and implementations bind them.
- trait.assoc.self
Self::Itemprojects from the current implementation. - trait.assoc.projection
T::Itemprojects from a generic type whose bounds select exactly one associated type declaration. - trait.assoc.ambiguous-typeA projection whose bounds select two different associated type declarations is ambiguous. An ambiguous projection is an error. Error:
ambiguous-associated-type. - trait.assoc.no-typeA projection whose bounds select no associated type declaration is an error. Error:
unknown-associated-type. - trait.assoc.boundA bound may also fix a projection to a type.
- trait.assoc.bound-declA trait's associated type may name bounds after
<, joined by&, as intype IntoIter < Iterator[T]. A bound may use the trait's parameters andSelf. - trait.assoc.bound-implThe type an implementation binds must implement each bound, with the trait's arguments and the implementing type substituted. A type that does not is an error. Error:
unsatisfied-trait-bound. - trait.assoc.bound-useUnder a bound
I < Tr[...], the projectionI::Ximplements each bound thatTrdeclares onX. So its methods are callable, and it satisfies a bound that names one of them.
trait Named:
type Item
trait Keyed:
type Item
fn pick[I < Named & Keyed](value: I) -> I::Item: # error: ambiguous-associated-type
panic("unreachable")trait Bag[T]:
type Cursor < Iterator[T]
fn cursor(self) -> Self::Cursor
fn total[B < Bag[i32]](bag: B) -> i32:
bag.cursor().sum::[i32]()
struct Words:
text: string
impl Bag[i32] for Words: # error: unsatisfied-trait-bound
type Cursor = string
fn cursor(self) -> string:
self.textSee also: Associated Type Bindings.
#Trait Implementations
This section defines the standard comparison, conversion, error, and debug traits, and the rules that every trait implementation follows.
#Comparison Traits
The standard library defines Eq, PartialOrd, Ord, and Ordering in
std.cmp.
| Rule | Trait | Requires |
|---|---|---|
| trait.cmp.equalityEquality | Eq |
fn eq(self, other: Self) -> bool |
| trait.cmp.partial-orderingPartial ordering | PartialOrd < Eq |
fn partial_cmp(self, other: Self) -> Ordering?, where .None means unordered |
| trait.cmp.total-orderingOrdering | Ord < PartialOrd |
fn cmp(self, other: Self) -> Ordering |
- trait.cmp.std-moduleThe standard library defines
Eq,PartialOrd,Ord, andOrderinginstd.cmp. - trait.cmp.ordering
OrderinghasLess,Equal, andGreatercases. - trait.cmp.one-eq
Eqis the only equality trait. There is no separate partial-equality trait. - trait.cmp.reflexiveAn
Eqimplementation must be reflexive:x.eq(x)istruefor every valuex. - trait.cmp.ord-agreeAn
Ordimplementation must agree with the type'sPartialOrdimplementation. - trait.cmp.lawsThese semantic laws are obligations of the implementer; ordinary trait checking cannot prove them.
- trait.cmp.float-eqFloating-point types implement
Eqwith IEEE 754 semantics, so NaN is unequal even to itself. This is the one documented exception to the reflexive law. - trait.cmp.float-ordFloating-point types implement
PartialOrdbut notOrd, because NaN is unordered. - trait.cmp.operators-comparison
==,!=, and the relational operators invoke the comparison traits. The other operators invoke thestd.opstraits of Operator Traits.
@derive(Eq, PartialOrd, Ord)
struct Level:
rank: i32
fn demo(a: Level, b: Level) -> bool:
a < bWhy. This follows Swift's
Equatable: one equality trait serves==, map keys, and derivation, and floats keep IEEE 754 equality.
#Hashing
The standard library also defines Hash and Hasher in std.hash:
trait Hasher:
fn write(&mut self, bytes: List[u8]) -> void
fn write_u8(&mut self, value: u8) -> void
fn write_u16(&mut self, value: u16) -> void
fn write_u32(&mut self, value: u32) -> void
fn write_u64(&mut self, value: u64) -> void
fn write_usize(&mut self, value: usize) -> void
fn write_i8(&mut self, value: i8) -> void
fn write_i16(&mut self, value: i16) -> void
fn write_i32(&mut self, value: i32) -> void
fn write_i64(&mut self, value: i64) -> void
fn write_str(&mut self, text: string) -> void
trait Hash:
fn hash(self, state: &mut dyn Hasher) -> void- trait.hash.moduleThe standard library defines
HashandHasherinstd.hash. - trait.hash.map-keyA map key must implement both
EqandHash. - trait.hash.not-inferredNeither trait is inferred for user-defined structs or enums, including payload-free enums.
- trait.hash.unverifiedThe compiler does not verify any relationship between their implementations.
- trait.hash.write-required
writeis the only required method ofHasher. - trait.hash.typed-methods
Hashersupplies default methodswrite_u8,write_u16,write_u32,write_u64,write_usize,write_i8,write_i16,write_i32,write_i64andwrite_stroverwrite. - trait.hash.typed-bytesEach integer method writes the value's bytes least significant first, using two's-complement representation for signed values. Widths are one byte for
i8andu8, two fori16andu16, four fori32,u32andusize, and eight fori64andu64. - trait.hash.string-bytes
write_str(text)writes the UTF-8 bytes oftext, with no length. - trait.hash.override-equivalentAn implementation may override a typed method, but it must give the same result as writing the specified bytes through
write. - trait.hash.ref-receiver
Hasher's methods take&mut self, andHash.hashreceives its hasher as&mut dyn Hasher. So a struct that implementsHasherchanges in place through a bound and through a trait value reference, and its caller reads the changed state afterwards.
Note. A key that holds a reference can still change through another reference to the same place, potentially leaving its map entry unreachable.
#Derived Implementations
- trait.derive.no-automaticThere is no automatic conformance for user-defined struct or enum types.
- trait.derive.explicit-implAn explicit implementation may choose domain-specific equality or ordering.
- trait.derive.intrinsic-decl
@derive(...), as in@derive(Eq, Hash), is an explicit compiler intrinsic on a struct, enum, or newtype declaration. - trait.derive.argumentsIts arguments name traits, not metadata values.
- trait.derive.generateThe compiler generates ordinary implementations of the named traits from the declaration's shape.
- trait.derive.checkThe compiler checks trait requirements and coherence, and rejects traits for which it has no derivation rule.
- trait.derive.boundsFor each derived trait, the generated implementation adds a
T < Traitbound for some declaration type parameters. That is everyTthat occurs in a field compared, ordered, or hashed by that derivation. - trait.derive.bounds.exampleThus
@derive(Eq) struct Box[T]produces conformance only whenT < Eq. - trait.derive.supertraitsThe target must also satisfy each derived trait's supertraits, whether through an existing implementation or another derivation.
- trait.derive.cmp-templates
Eq,PartialOrd,Ord, andHashderive through their templates, as every derivable trait does. The compiler writes none of their bodies. - trait.derive.cmp-every-memberA derivation of
Eq,PartialOrd,Ord, orHashcovers every member, and no member line or derivation block configures it. - trait.derive.templated
@deriveaccepts a trait that has a derivation template, as Typed Derivation defines. Any other trait is an error. Error:underivable-trait. - trait.derive.field-missing-traitA field that a derived
Eq,PartialOrd,Ord, orHashcompares, orders, or hashes must implement the derived trait. A field that does not is an error at the field, whose message names the trait and the field. A field that lacks several of the derived traits gets one error, naming the first of them in the@derivelist. Error:derive-field-missing-trait. - trait.derive.field-missing-trait.templateFor these four traits, this error replaces the template's
member-not-derivableofannot.walker.obligation.error. - trait.derive.bound-unmetA use of a derived implementation whose type argument does not meet a bound that
trait.derive.boundsadded is an error at the use. One case is==on twoBox[fn() -> void]values. Error:missing-derived-bound. - trait.derive.ownerA derived implementation is owned by the module that declares its target, whichever module declares the trait.
- trait.derive.beside-writtenDeriving a trait for a type that also has a hand-written implementation of that trait is an error, reported on the
@deriveline. Error:overlapping-impl. - trait.derive.reference-fieldsA reference field,
*Tor*mut T, implementsEqandHashwhenTdoes, by Reference Equality. So a derivation of either accepts it then, and a type that refers to itself through references may derive both. - trait.derive.no-cycle-checkA derivation is allowed whether or not the values of its type can form a cycle through references. The compiler checks no cycle.
- trait.derive.reference-debugA reference field implements
Debugwhen the type it refers to does, by References. So@derive(Debug)accepts it then, as@derive(Eq)does.PartialOrdandOrdhave no implementation for references, so a derivation of either rejects a reference field. Error:derive-field-missing-trait.
struct Opaque: pass
@derive(Eq, Hash)
struct Key:
value: Opaque # error: derive-field-missing-trait
@derive(Eq)
struct Box[T]:
value: T
fn same(left: Box[fn() -> void], right: Box[fn() -> void]) -> bool:
left == right # error: missing-derived-bound@derive(Eq) # error: overlapping-impl
struct Badge:
text: string
impl Eq for Badge:
fn eq(self, other: Badge) -> bool:
self.text == other.textNote. What each derived implementation compares, orders, or hashes is stdlib tier: Cmp and Hash. The checks above stay in the language tier.
Note.
std.opsalso declares a template forDefault, so@derive(Default)builds each member's default, and an enum marks its default variant with@default(Derived Default).
Note. The reference prototype still writes these four bodies in the compiler. It moves them to the templates once its template lowering no longer re-checks generated source. Programs see no difference.
#Related Traits
The comparison and hash traits whose laws relate them are related traits:
| Rule | Trait | Related traits |
|---|---|---|
| trait.derive.related.hashHash | Hash |
Eq |
| trait.derive.related.partial-ordPartial ordering | PartialOrd |
Eq |
| trait.derive.related.ordOrdering | Ord |
Eq and PartialOrd |
- trait.derive.related.same-listDeriving
Hash,PartialOrd, orOrdrequires each of its related traits to be derived in the same@derivelist. - trait.derive.related.no-mixOne type's related traits must be all derived or all hand-written: a derived implementation and a hand-written implementation of two related traits never coexist.
- trait.derive.related.errorA derivation that breaks either rule is an error, reported on its
@deriveline. Error:mixed-derived-law. - trait.derive.related.replacesWhen a derived trait's missing related trait is also its supertrait, as
PartialOrdis forOrd, this error replacesmissing-supertrait-implementationon that@deriveline.
@derive(Hash) # error: mixed-derived-law
struct Session:
token: string
impl Eq for Session:
fn eq(self, other: Session) -> bool: self.token == other.tokenWhy. A hand-written
Eqthat ignores a field, beside a derivedHashthat hashes it, would put equal keys in different buckets.
#Derived Newtypes
- trait.derive.newtype
@deriveon a newtype declaration generates each named trait's implementation from the base type's implementation. - trait.derive.newtype.baseThe generated method applies the base type's method to the wrapped values, as in
Mile(1) == Mile(1)comparing the twoi32values. - trait.derive.newtype.requiresThe base type must implement each derived trait, as a derived field must.
- trait.derive.newtype.requires.errorA base type that does not implement a derived trait is an error at the base type. The message names the trait and the base type. Error:
derive-field-missing-trait. - trait.derive.newtype.not-inheritedA newtype still inherits no implementation it does not derive or implement.
- trait.derive.newtype.templatedA trait with a template also derives through the base type: the newtype gets no
Structure, and the base type's implementation is rewrapped. - trait.derive.newtype.self-positionsForwarding is allowed only where the trait's methods use
Selfas the receiver, as plainSelf, or insideSelf?,Result[Self, E], orList[Self]. - trait.derive.newtype.self-errorAny other position, such as
Map[Self, V],Map[string, Self], or a tuple holdingSelf, is an error at the@deriveline that names the trait method. Error:newtype-derivation-self.
@derive(Eq, Hash)
type Mile(i32)
fn same(left: Mile, right: Mile) -> bool:
left == righttrait Pairing:
fn pair(self) -> (Self, i32)
@derive(Pairing) # error: newtype-derivation-self
type Tag(string)
struct Opaque: pass
@derive(Eq)
type Wrapped(Opaque) # error: derive-field-missing-traitWhy. A
Map[Self, V]result depends on the key's ownHashandEq, which a rewrapped base value would not use. The allowed positions rewrap each value one at a time.
See also: Newtypes.
#Derived Equality
Note. This heading keeps its name so that links to it stay valid. What a derived
Eqcompares is stdlib tier since batch 36: Derived Equality instd.cmp.
#Derived Ordering
Note. This heading keeps its name so that links to it stay valid. What a derived
PartialOrdorOrdcompares is stdlib tier since batch 36: Derived Ordering instd.cmp.
#Derived Hashing
Note. This heading keeps its name so that links to it stay valid. What a derived
Hashhashes is stdlib tier since batch 36: Derived Hashing instd.hash.
#Conversion Trait
The standard library declares the general conversion trait From in
std.convert:
trait From[T]:
fn from(value: T) -> Self- trait.from.moduleThe standard library declares the general conversion trait
Frominstd.convert. - trait.from.meaningAn implementation
impl From[T] for Xconverts aTinto anX. - trait.from.import
Fromis not a prelude name; code that names it imports it, as inuse std.convert.From. - trait.from.propagationPostfix
?uses the trait without an import. When an error is not assignable to the enclosing function's error type,?calls that error type'sFromimplementation once. - trait.from.directAny code may also call a conversion directly as
X::from(value).
See also: Propagation.
#Pure Conversions
- trait.from.pureA conversion is pure.
- trait.from.rowThe trait method
fromhas the empty requirement row and is not suspending. An implementation method must agree with it on both. - trait.from.signatureAn implementation whose
fromdeclares a requirement clause, as infn from(value: FsError) -> SyncError $ Console, is an error. So is one whosefromis suspending, as infn from!(value: FsError) -> SyncError. Error:trait-method-signature. - trait.from.panicThe body of
frommay still panic.
impl From[FsError] for SyncError:
fn from(value: FsError) -> SyncError $ Console: # error: trait-method-signature
println("converting")
SyncError.Fs(value)See also: Requirement Rows.
#Conversion Implementations
- trait.from.coherence
Fromimplementations follow the ordinary rules for implementation targets, ownership, overlap, and uniqueness. - trait.from.owners
impl From[FsError] for SyncErrormay be declared by the package that ownsSyncError, the package that ownsFsError, or the standard library. - trait.from.no-overlapImplementations for different source types never overlap, because their trait arguments differ. One error type may therefore implement both
From[FsError]andFrom[HttpError]. - trait.from.reflexiveA reflexive
impl[T] From[T] for Tis an error. Error:bare-parameter-impl-target. - trait.from.trait-valueA trait value type is never a target, so
impl From[FsError] for Erroris an error. Error:trait-value-impl-target. - trait.from.instantiationsWhen
XimplementsFromat several instantiations,X::from(value)chooses among them by the rule for instantiations of one generic trait in Method Resolution. - trait.from.candidatesEach instantiation is a candidate, and the one that applies to the call is selected.
- trait.from.no-applyWhen no instantiation applies, the call is an error. Error:
type-mismatch.
use std.convert.From
struct AppError:
message: string
impl From[i32] for AppError:
fn from(code: i32) -> AppError:
AppError { message: "code $code" }
impl From[string] for AppError:
fn from(text: string) -> AppError:
AppError { message: text }
fn demo(code: i32) -> AppError:
AppError::from(code) # the i32 instantiation is selected#Error Trait
The standard library declares the standard error trait Error in
std.error.
- trait.error.moduleThe standard library declares the standard error trait
Errorinstd.error. - trait.error.supertraits
ErrorhasDisplayand the sealedInspectableas supertraits, as intrait Error < Display & Inspectable. - trait.error.defaultsEvery member
Errordeclares has a default, so an implementation needs no body. - trait.error.complete
impl Error for FsErroris complete when the enumFsErrorimplementsDisplay, because the compiler suppliesInspectablefor it. - trait.error.not-inspectableAn
impl Errorwhose target is not inspectable, such as a type declared in a block suite, is an error. Error:missing-supertrait-implementation. - trait.error.value-typesA value type may implement
Error, as an enum or a newtype overstringdoes. - trait.error.reference-source
Errordeclaresfn source(self) -> (*dyn Error)?, which returns the error that caused this one. Its default returns.None. - trait.error.dyn-methods.reference-values
std.errorgives&dyn Errorthe inherent methodschain,root_cause, andfind[T < Error], in animpl dyn Error:block beside the trait. - trait.error.dyn-methods.not-membersThey are not members of
Error, so an implementation ofErrorcannot declare or override them. - trait.error.dyn-methods.reference-receiverThey take shared reference receivers, as in
let erased: *dyn Error = &failure; erased.find::[FsError](). - trait.error.inherent-recovery.reference-valuesThe inherent
impl dyn Errordeclares the recovery methods ofimpl dyn Inspectable, with identical reference signatures. - trait.error.std-apiWhat
chain,root_cause, andfindreturn is standard-library API. - trait.error.deriveA struct or enum may implement
DisplayandErrorthrough the@errorintrinsic, as Error Derivation defines. - trait.error.import
Erroris not a prelude name; code imports it withuse std.error.Error. - trait.error.no-inspect-importImplementing
Errorneeds no import ofstd.inspect.
use std.error.Error
fn local() -> void:
enum LocalError:
Failed
impl Display for LocalError:
fn to_string(self) -> string: "failed"
impl Error for LocalError # error: missing-supertrait-implementation
passWhy. Most errors are enums, which are values without identity, so
Errorhas noAnyRefsupertrait.findstill works on an erased error, because a method type parameter may take any type argument. As inherent methods of&dyn Error, the chain helpers mean the same thing for every error.
See also: Sealed Traits,
Error Derivation,
Cause Chain and
Root Cause And Find in std.error.
#Erased Errors
- trait.error.erased.reference-valuesThe dynamic trait value
*dyn Erroris the erased application error. - trait.error.result.reference-valuesA
Result[T, *dyn Error]holds any error that implementsError.?reaches it by reference erasure, sharing a reference or copying a value into a new object. - trait.error.convert.reference-valuesA concrete counted error reference converts implicitly to
*dyn Error; an ordinary value requires a written&, as Trait Value References specifies. - trait.error.entry-point.error-boundAn erased
*dyn ErrorsatisfiesErrorand its supertraits, soResult[T, *dyn Error]implementsTerminationwhenT < Termination. - trait.error.boundary.reference-valuesLike every dynamic trait value, an erased
*dyn Erroris not boundary-safe and never crosses a registered boundary. - trait.error.convert-first.reference-valuesCode converts an erased
*dyn Errorexplicitly to a boundary-safe error type first. - trait.error.reference-recoveryBecause
ErrorextendsInspectable, a reference to an erased error recovers its concrete struct or enum through its inherent recovery methods.
use std.error.Error
enum FsError:
NotFound(path: string)
impl Display for FsError:
fn to_string(self) -> string:
match self:
FsError.NotFound(path) => "not found: " + path
impl Error for FsError
fn read_config(path: string) -> Result[string, FsError]:
.Err(FsError.NotFound(path))
fn load(path: string) -> Result[string, *dyn Error]:
text := read_config(path)?
.Ok(text)See also: Propagation, Dynamic Trait Values, Wasm Boundary, Runtime Type Identity.
#Debug Trait
The standard library declares Debug in std.format, beside Display, to
show a value's structure:
trait Debug:
fn debug(self, out: *mut DebugWriter) -> voidA type usually derives it:
@derive(Eq, Debug)
struct Point:
x: i32
y: i32
fn describe(point: Point) -> string:
debug(point)- trait.debug.module
std.formatdeclaresDebugandDebugWriter.Debugis a prelude name. - trait.debug.method-ref
Debugdeclaresfn debug(self, out: *mut DebugWriter) -> void, which writes the value's structure throughout, a reference to the one writer of the whole value. - trait.debug.writer
DebugWriteris the standard structured writer. An implementation describes the value through its builder calls, such as one call per field, rather than raw text. - trait.debug.std-impls
stdimplementsDebugfor the primitives, collections,T?, andResult, each when its type arguments implementDebug. - trait.debug.tuples
std.formatdeclares a tuple template forDebug, so every tuple whose elements implementDebugimplements it, at every size. - trait.debug.tuples.restFor a tuple with a rest element,
Tmust implementDebug. Its rest element's items are written inline after the fixed elements. So a value(1, 2, 3, 4)of type(i32, i32, List[i32]...)writes as the tuple(1, 2, 3, 4)of fouri32does. - trait.debug.derive
@derive(Debug)derivesDebugthrough its template. The derived implementation walks the declaration's members and writes each one. - trait.debug.not-display
Debugis separate fromDisplay, which stays user-facing text. - trait.debug.writer-import
DebugWriteris not a prelude name, so a hand-written implementation imports it, as inuse std.format.DebugWriter.
Note. The text that
debugreturns, theDebugWriterbuilders, and the builder calls that@derive(Debug)generates are stdlib tier: Format.
See also: Debug Builders in the stdlib tier, Standard Testing, Typed Derivation.
#Literal Suffix Trait
Note. This heading keeps its name so that links to it stay valid.
std.ops.LiteralSuffixand its newtype carriers were removed by Literal Suffixes L11 (2026-09-28): a suffix is a function marked@num_suffix, as Literal Suffixes defines.
#Implementation Declarations
An explicit implementation names the trait and target type:
impl Display for User:
fn to_string(self) -> string:
self.email- trait.impl.explicitAn explicit implementation names the trait and target type.
- trait.impl.requiredThe implementation must write every required method not supplied by a default, and bind every associated type of the trait. Omitting one is an error. Error:
missing-trait-method. - trait.impl.fillOnly a method written in the implementation or a trait default fills a trait method.
- trait.impl.fill.neverAn inherent method of the target and a method promoted from an embedded field never fill a trait method, required or defaulted, whatever its receiver. So neither makes a body optional.
- trait.impl.override-opaqueThe implementation may override a default with the instantiated signature, allowing the opaque-result refinement below.
- trait.impl.signatureA mismatched method is an error. Error:
trait-method-signature. - trait.impl.extra-methodsAdditional methods do not become part of that trait implementation; place them in an inherent
implinstead. A method or associated type that the trait does not declare is an error. Error:extra-trait-member. - trait.impl.uniqueAt most one implementation of the same instantiated trait for the same target type may exist in a resolved program.
trait Named:
fn name(self) -> string
struct User: pass
impl Named for User # error: missing-trait-method
struct Admin: pass
impl Named for Admin:
fn name(self) -> string: "admin"
fn shout(self) -> string: "ADMIN" # error: extra-trait-member#Opaque Method Results
A trait method may hide its result behind an impl Trait bound.
- trait.opaque.impl-resultAn implementation may return a concrete type or its own opaque result in place of the trait method's opaque occurrence.
- trait.opaque.impl-boundThe chosen result must satisfy that occurrence's instantiated bound and associated-type bindings. Error:
unsatisfied-trait-bound. - trait.opaque.impl-shapeThe remaining result structure must match the trait declaration. Error:
trait-method-signature. - trait.opaque.impl-identityEach trait-method opaque occurrence has a static identity determined by the method, implementation target, trait arguments, method arguments and occurrence position.
- trait.opaque.generic-boundA caller through a generic bound sees only the opaque occurrence's declared bounds.
- trait.opaque.codegen-selectedCode generation resolves the occurrence to the selected implementation's result, using the trait's default body when no override exists.
Why. Different implementations can return different concrete types while generic callers depend only on the written interface.
#Method Generic Parameters
- trait.impl.genericsThe exact signature includes the method-level generic parameters.
- trait.impl.generics.countAn implementation method declares as many generic parameters as the trait method, and they correspond by position; names may differ.
- trait.impl.generics.markersEach parameter keeps the trait method's bounds.
- trait.impl.generics.boundsThe same bounds are the same traits, with the same instantiated arguments and associated type bindings, written in the same order.
- trait.impl.generics.defaultEach parameter also repeats the trait method's default, instantiated as the bounds are, or has none when the trait method's has none. A missing, extra, or different default is a mismatch.
- trait.impl.generics.fixed-boundsAn implementation method therefore cannot add, drop, reorder, weaken, or strengthen a bound. The one exception is the strengthened member bound of a walker, describer, or source, which
annot.walker.strengthen-memberallows. - trait.impl.generics.errorAny mismatch is an error reported at the implementation method. Error:
trait-method-signature.
trait Show:
fn show(self) -> string
trait Describe:
fn describe[T](self, value: T) -> string
struct Describer: pass
impl Describe for Describer:
fn describe[T < Show](self, value: T) -> string: value.show() # error: trait-method-signature#Method Parameter Defaults
- trait.impl.param-defaultA trait method's parameter defaults come from the trait declaration.
- trait.impl.param-default.callsA call that omits such an argument evaluates the trait's default, whichever body the call selects: the trait's default body or an implementation's override. This holds for a concrete receiver, a generic bound, and a trait value.
- trait.impl.param-default.no-restateAn implementation method declares no parameter defaults. A default written there is an error. Error:
trait-method-signature.
trait Check:
fn limit(self, max: usize = 64) -> usize: max
struct Item: pass
impl Check for Item
struct Strict: pass
impl Check for Strict:
fn limit(self, max: usize) -> usize: max / 2 # Strict {}.limit() is 32
struct Loose: pass
impl Check for Loose:
fn limit(self, max: usize = 128) -> usize: max # error: trait-method-signatureWhy. As in Rust, the signature belongs to the trait. A caller through a bound or a trait value sees only the trait's declaration, so a default restated in an implementation could never apply consistently.
#Local Implementations
- trait.impl.localAn
implinside an executable block suite is a compile-time declaration. - trait.impl.local.traitA local trait implementation must involve a local trait or a local nominal target type visible at its declaration point.
- trait.impl.local.inherentA local inherent implementation must target a local nominal type. Error:
local-impl-nonlocal-pair. - trait.impl.local.nonlocalImplementations for a pair of nonlocal types belong at module scope. A local one is an error. Error:
local-impl-nonlocal-pair. - trait.impl.local.checksLocal implementations obey the same target, ownership, overlap, and uniqueness checks as module-level implementations.
- trait.impl.local.no-secondLexical scope does not permit a second implementation for an existing pair.
- trait.impl.local.no-captureLocal methods and local-trait default methods cannot capture enclosing runtime values. Such a capture is an error. Error:
unknown-name. - trait.impl.local.lookupTheir methods are available for lookup from the local
impldeclaration point through its enclosing suite and child scopes, not before or outside that scope.
fn demo() -> string:
trait Shout:
fn shout(self) -> string
struct Whisper:
text: string
impl Shout for Whisper:
fn shout(self) -> string: self.text
Whisper { text: "hi" }.shout()#Intrinsic Methods
An intrinsic method is an implementation method whose body the compiler supplies. The standard library writes each primitive operation as one, such as the addition of every number type:
impl[N < Num] Add for N:
type Out = N
@intrinsic
fn add(self, rhs: N) -> N- trait.impl.intrinsicAn implementation method written after the decorator line
@intrinsichas no body. It is an intrinsic method, and the compiler supplies its body. - trait.impl.intrinsic.std-onlyOnly the standard library sees the name
intrinsic. In any other package,@intrinsicnames no binding. Error:unknown-name. - trait.impl.intrinsic.operationEach intrinsic method computes one primitive operation, such as integer addition, for every type that its implementation covers.
- trait.impl.intrinsic.rulesEvery other rule for implementation methods applies to an intrinsic method, including its exact signature.
- trait.impl.intrinsic.inlineAn implementation may compile a call of an intrinsic method to inline code, such as the machine addition for
a + bon twoi32operands. This is an optimization and never changes a result.
use std.ops.Add
struct Money:
cents: i64
impl Add for Money:
type Out = Money
@intrinsic # error: unknown-name
fn add(self, rhs: Money) -> MoneyWhy. An operator always calls its trait method, so
a + binsideAdd.addwould call itself. An intrinsic method is the base case that the operator traits rest on, written once per operation.
See also: Primitive Implementations, Standard Library Primitives.
#Implementation Targets
The target of every implementation, trait or inherent, starts with a type constructor.
| Rule | Type constructor | Examples |
|---|---|---|
| trait.target.declarationDeclared | a struct, enum, or newtype declaration | User, Box |
| trait.target.builtinBuilt-in | a built-in type constructor | i32, string, List, Map |
| trait.target.tupleTuple | a tuple constructor, one per arity | (A, B), the two-element tuple constructor applied to A and B |
| trait.target.function-typeFunction | a function type constructor, Fn or SuspendFn |
fn(i32) -> i32, which is Fn[(i32,), i32, $()], and SuspendFn[Args, O, $ R] |
- trait.target.constructorThe target of every implementation, trait or inherent, starts with a type constructor from the table above.
- trait.target.tuple.valid
impl Display for (i32, string)is a valid target. - trait.target.tuple.arityTuples of different arity never share a constructor.
- trait.target.optionAn optional target is the prelude enum
Optionapplied to its contained type.impl Validate for string?targetsOption[string], and by Overlap it does not overlap an implementation fori32?. - trait.target.argumentsThe constructor's arguments may be any types, including implementation parameters, as in
impl[T < Display] Printable for Box[T]. - trait.target.bare-parameterA target that is a bare type parameter, as in
impl[T] Describe for T, is an error. Error:bare-parameter-impl-target. - trait.target.numeric-familyThe one exception is a standard-library implementation in which every type parameter is bounded by exactly one of
Num,Integer, andFloat, as inimpl[N < Num] Add for N. - trait.target.numeric-family.eachSuch an implementation stands for one implementation per way of replacing each parameter with a type that Sealed Traits lists for its bound.
- trait.target.numeric-family.per-typeSo
impl[N < Integer] Shl[u32] for Nstands for 9 implementations, one per integer type. - trait.target.no-blankethd-lang has no blanket implementations over every type.
- trait.target.function-type.validA function type is an ordinary target under the ownership and overlap rules below, so
impl Marker for fn(i32) -> i32is valid in the package that declaresMarker. - trait.target.row-argumentA row argument in an implementation head, such as a function type's row, is a row parameter or a concrete row.
- trait.target.row-argument.extensionA row argument that lists a row parameter beside other keys, as in
Fn[(), i32, $ R + Log], is an error in an implementation head. Error:invalid-impl-target. - trait.target.trait-valueA trait value type is never an implementation target either:
Displayused as a type names a dynamic trait value, not a type constructor. - trait.target.trait-value.error
impl Marker for Displayandimpl Marker for Anyare errors. Error:trait-value-impl-target. - trait.target.trait-value.argument.reference-valuesA trait value type may still be a constructor's argument, as in
impl Marker for List[*dyn Display]. - trait.target.no-mutA target must not be written with an outer
mut:impl Marker for mut Counteris an error. Error:mutable-impl-target. - trait.target.templateA derivation template, written
impl[T] Trait for T by Structure, is not an implementation, so these target rules do not apply to it. Each derivation it produces is an ordinary implementation for a declared target, as Templates defines.
trait Describe:
fn describe(self) -> string
trait Marker
struct Counter:
value: i32
impl[T] Describe for T: # error: bare-parameter-impl-target
fn describe(self) -> string:
"value"
impl Marker for &dyn Display # error: trait-value-impl-target
impl Marker for mut Counter # error: mutable-impl-targetA function type is a target like any other:
use std.function.{Fn, Tuple}
trait Describe:
fn describe(self) -> string
impl[Args < Tuple, O, $R] Describe for Fn[Args, O, $ R]:
fn describe(self) -> string:
"function"Note. A later revision may add blanket implementations as a compatible extension.
#Derived Tuple Implementations
Tuples derive traits through tuple templates, at every size:
fn compare(a: (i32, string), b: (i32, string)) -> bool:
a == b || a < b
fn index(counts: Map[(i32, string), i32]) -> i32:
counts[(1, "a")]- trait.target.tuple.templatesEvery tuple type, with or without a rest element, derives each trait that has a tuple template. It does so when its elements meet that template's obligation.
- trait.target.tuple.no-limitNo tuple trait stops at a fixed number of elements.
Note.
stddeclares tuple templates forEq,PartialOrd,Ord, andHash(Tuple Comparison, Tuple Hashing), forDebugandDisplay, and forDefault(Standard Implementations). These are the tuple equality ofexpr.eq.std, the tuple order ofexpr.ord.std.sequences, and the hashable tuple keys oftypes.map-key.builtin-hash.
Note.
List[T]implementsHashwhenTdoes (types.map-key.builtin-hash). So a tuple with a rest element is a hashable map key when its elements and its rest's items are hashable.
#Implementation Ownership
An impl Trait[Args] for Target may be declared only in a package that owns
one of these declarations:
| Rule | Owned declaration |
|---|---|
| trait.own.traitTrait | the trait |
| trait.own.targetTarget | the target's outer type constructor |
| trait.own.argumentTrait argument | the outer type constructor of one of the trait arguments Args |
- trait.own.ruleAn
impl Trait[Args] for Targetmay be declared only in a package that owns one of the declarations in the table above. - trait.own.orphanAny other trait implementation is an error. Error:
orphan-impl. - trait.own.no-orphan-exceptionNo package, including the root application package, has an orphan exception.
- trait.own.argument.exampleFor example, the package that declares
Moneymay writeimpl Add[Money] for i32, because it owns the trait argumentMoney. - trait.own.bare-parameterThe trait-argument case never applies to a target that is a bare type parameter.
- trait.own.aliasesTransparent aliases do not create ownership; nominal newtypes do.
- trait.own.stdThe standard library owns primitives, built-in collection type constructors, tuple constructors, and the prelude enums
OptionandResult. - trait.own.optionalAn implementation for
string?therefore needs the package of the trait or of a trait argument. An example isimpl Validate for string?in the package that ownsValidate. - trait.own.std.functionThe standard library also owns the function type constructors
FnandSuspendFn. An implementation for a function type therefore needs the package of the trait or of a trait argument. - trait.own.inherentAn inherent implementation may be declared only in the package that owns its target nominal type. Error:
orphan-impl. - trait.own.inherent.target-formsAn inherent implementation cannot target a tuple, a transparent alias, or a type owned by another package. A
dyntype is a target only as Inherent Methods OndynTypes allows. - trait.own.inherent.tuple-aliasAn inherent implementation whose target is a tuple or a transparent alias is an error, whichever package owns the type it names. Error:
invalid-impl-target. - trait.own.inherent.stdThe standard library, which owns them, may declare inherent implementations for primitives, built-in collection type constructors, and the prelude enums
OptionandResult. - trait.own.inherent.std.no-useTheir
pubmembers are found by ordinary member lookup on the receiver's type, so calling one needs nouse. - trait.own.inherent.std.no-tupleTuples have no inherent members, including from the standard library; they get only trait implementations. A member that no trait supplies is an error. Error:
unknown-method. - trait.own.graphThe compiler must also reject a resolved dependency graph containing duplicate exact implementations. This includes the possible conflict where two owning packages each provide the same pair.
impl Display for i32: # error: orphan-impl
fn to_string(self) -> string:
"$self"
impl Display for fn() -> i32: # error: orphan-impl
fn to_string(self) -> string:
"function"
impl i32: # error: orphan-impl
fn twice(self) -> i32:
self * 2
impl (i32, i32): # error: invalid-impl-target
fn sum(self) -> i32:
self._0 + self._1
struct User:
name: string
type Person = User
impl Person: # error: invalid-impl-target
fn label(self) -> string:
self.nameWhy. These ownership rules prevent downstream packages from creating globally surprising conformance.
#Implementation Modules
- trait.own.module.inherent-targetAn inherent implementation must be declared in the module that declares its target type, except as
trait.own.module.inherent.stdallows. - trait.own.module.inherent.stdAn inherent implementation that
trait.own.inherent.stdallows may be declared in any module of the standard library. - trait.own.module.traitA trait implementation must be declared in a module that declares one of three things. They are the trait, the target's outer type constructor, or the outer type constructor of a trait argument that gives its package ownership.
- trait.own.module.errorAn implementation declared in any other module of the owning package is an error. Error:
nonlocal-impl. - trait.own.module.generatedA derived implementation is generated in the module of its declaration, so it always satisfies these rules.
trait Greet:
fn greet(self) -> string
struct User:
name: string
impl Greet for User: # same module as User: fine
fn greet(self) -> string: "hi"Why. A reader of the type's or the trait's module sees every implementation that can answer a call. No distant module of the package can add one.
#Inherent Methods On dyn Types
The module that declares a trait may give its dyn type inherent methods:
trait Shape:
fn area(self) -> f64
impl dyn Shape:
fn double(self) -> f64:
self.area() * 2.0
struct Square:
side: f64
impl Shape for Square:
fn area(self) -> f64: self.side * self.side
fn report(shape: &dyn Shape, square: Square) -> f64:
shape.double() + square.double() # error: unknown-method- trait.own.dyn-inherent.formAn inherent implementation may target a
dyntype, as inimpl dyn Shape:. Its methods are inherent methods of thatdyntype. - trait.own.dyn-inherent.moduleIt must be declared in the module that declares the trait. Elsewhere in the trait's package it is an error. Error:
nonlocal-impl. - trait.own.dyn-inherent.packageIn another package it is an error. Error:
orphan-impl. - trait.own.dyn-inherent.receiverIts methods may be called only on a value whose type is that
dyntype. A concrete type that implements the trait does not have them, and neither does a type parameter bounded by the trait. Error:unknown-method. - trait.own.dyn-inherent.no-overrideThey are not members of the trait, so an implementation of the trait cannot declare or override them.
- trait.own.dyn-inherent.no-trait-implA trait implementation for a
dyntype stays an error, astrait.target.trait-value.errorstates.
Why. Helpers that only make sense on an erased value, such as searching an error chain, belong to the
dyntype, as in Rust'simpl dyn Error. Keeping them out of the trait means no implementation can change what they do.
#Overlap
Implementations may be generic and state their bounds inline in the generic parameter list:
impl[T < Display] Printable for Box[T]:
fn print(self) -> string:
self.value.to_string()- trait.overlap.genericImplementations may be generic and state their bounds inline in the generic parameter list.
- trait.overlap.constrained-headEvery type parameter of a generic implementation must be constrained. That means it appears in the implemented trait's arguments or in the target type, or an associated-type binding fixes it. A parameter that appears only in another bound is an error. One case is
Tinimpl[T < Display, I < Holder[T]] Summary for Feed[I]. Error:unconstrained-impl-parameter. - trait.overlap.constrained-bindingAn associated-type binding in the bound of a constrained parameter constrains the parameter it names, as
Tinimpl[T < Display, I < Store[Item = T]] Summary for Feed[I]. - trait.overlap.head-projectionAn implementation's trait arguments and target type must not contain a projection of one of its type parameters, such as
I::Item. Such a head is an error. Error:unconstrained-impl-parameter. - trait.overlap.definitionTwo implementations overlap when they implement the same trait and their full heads unify.
- trait.overlap.unifyHeads unify when, after each implementation's parameters are renamed apart, one substitution makes both their trait arguments and their complete target types equal.
- trait.overlap.heads-onlyOverlap is decided from the implementation heads alone.
- trait.overlap.no-boundsBounds, including associated type bindings, are never used to claim that two implementations are disjoint.
- trait.overlap.numeric-familyAn implementation with a numeric-family target overlaps another implementation when one of the implementations it stands for does.
- trait.overlap.errorOverlapping implementations are an error. Error:
overlapping-impl.
| First implementation | Second implementation | Overlap |
|---|---|---|
impl[T] Marker for List[T] |
impl Marker for List[i32] |
yes |
impl[T] Marker for Box[T] |
impl Marker for Box[i32] |
yes |
impl Marker for Box[i32] |
impl Marker for Box[string] |
no |
impl Add[i32] for Money |
impl Add[Money] for Money |
no |
impl[Args < Tuple, O, R] Marker for Fn[Args, O, $ R] |
impl Marker for fn(i32) -> i32 |
yes |
impl Marker for fn(i32) -> i32 |
impl Marker for fn(string) -> i32 |
no |
struct Box[T]:
value: T
struct Plain:
value: i32
trait Marker
impl[T < Display] Marker for Box[T]
impl Marker for Box[Plain] # error: overlapping-impltrait Holder[T]:
fn get(self) -> T
trait Summary:
fn summary(self) -> string
struct Feed[I]:
items: I
impl[T < Display, I < Holder[T]] Summary for Feed[I]: # error: unconstrained-impl-parameter
fn summary(self) -> string:
"items"trait Store:
type Item
fn first(self) -> Self::Item
trait Summary:
fn summary(self) -> string
struct Feed[I]:
items: I
impl[T < Display, I < Store[Item = T]] Summary for Feed[I]: # valid: the binding fixes T
fn summary(self) -> string:
"first: ${self.items.first()}"trait Store:
type Item
fn first(self) -> Self::Item
trait Summary:
fn summary(self) -> string
struct Feed[I]:
items: I
impl[I < Store] Summary for Feed[I::Item]: # error: unconstrained-impl-parameter
fn summary(self) -> string:
"items"Why. Once
Iis known, itsItemis known, soThas one value, as Rust's E0207 rule allows. A projection in a head runs the other way. Two typesImay share oneItem, so such a head can be neither matched nor checked for overlap.
Note. Because bounds are ignored, an implementation added later in a dependency cannot make two existing implementations overlap. A type parameter such as
Argsunifies with every tuple type, and a row parameter unifies with every row.
#Inherent Implementations
An inherent implementation, written impl T: without a trait, declares
members attached directly to the nominal type T:
impl User:
fn domain(self) -> string:
self.email.slice(1, self.email.len())#Inherent Members
- trait.inherent.declAn inherent implementation, written
impl T:without a trait, declares members attached directly to the nominal typeTrather than through a trait. - trait.inherent.membersIts members are the type's inherent members, which the table below defines.
- trait.inherent.ownerOnly the package that owns
Tmay declare them. - trait.inherent.vs-traitAn inherent method differs from a trait method, which a trait declares and a trait implementation supplies for
T.
| Member | Receiver | Called |
|---|---|---|
| inherent method | a receiver as its first parameter | with dot syntax, as in user.domain() |
| inherent associated function | none | through the type, as in User::guest() |
struct Counter:
value: i32
impl Counter:
fn read(self) -> i32: self.value
fn zero() -> Counter: Counter { value: 0 }See also: Implementation Ownership, Member Resolution.
#Method Visibility
- trait.inherent.privateInherent methods and inherent associated functions are module-private unless individually marked
pub, including when their nominal type is public. - trait.vis.trait-methodsMethods in trait declarations and trait implementations follow the trait's visibility.
- trait.vis.no-pub
pubis not written on an individual trait method or its implementation. - trait.vis.uniformA trait has one visibility level for all its methods; it cannot mix public and private methods.
struct Counter:
value: i32
impl Counter:
pub fn read(self) -> i32: self.value#Inherent Member Names
- trait.inherent.unique-unifyingTwo inherent members with one name are an error when their implementations' targets unify, including two members of one implementation. Error:
duplicate-inherent-member. - trait.inherent.unique-unifying.disjointInherent implementations of one type constructor whose targets cannot unify may repeat a member name.
impl Box[i32]andimpl Box[string]may each declareshow, butimpl[T] Box[T]andimpl Box[i32]must not. - trait.inherent.unique-unifying.unifyTargets unify as implementation heads do in Overlap, after renaming each implementation's parameters apart.
- trait.inherent.target-matchAn inherent member is a member of a receiver's type only when its implementation's target matches that type. So
box.show()on aBox[string]calls the member ofimpl Box[string]. - trait.inherent.field-namesAn inherent member may share its name with a field of the type, named or embedded, because fields and methods are separate namespaces.
- trait.inherent.no-overloadinghd-lang has no method or associated-function overloading.
struct User: pass
impl User:
fn name(self) -> string: "first"
fn name(self) -> string: "second" # error: duplicate-inherent-member
struct Box[T]:
value: T
impl[T] Box[T]:
fn show(self) -> string: "box"
impl Box[i32]:
fn show(self) -> string: "int box" # error: duplicate-inherent-memberSee also: Member Resolution.
#Inherent Associated Functions
Inherent associated functions are called through the nominal type:
impl User:
fn guest() -> User:
User { name: "guest" }
guest := User::guest()- trait.inherent.assoc-callInherent associated functions are called through the nominal type.
#Method Resolution
This section defines which trait methods are usable at a use and how trait candidates are reported.
- trait.resolve.lookupFor a receiver of nominal type
S,value.method(args)selects a method with the method lookup in Member Resolution. - trait.resolve.lookup.kindsThat lookup covers inherent methods, trait methods, and promoted methods together.
- trait.resolve.no-fieldIt never selects a field.
struct Counter:
value: i32
impl Counter:
fn read(self) -> i32: self.value
fn demo(c: Counter) -> i32:
c.read()#Trait Availability
For a concrete receiver, a trait is available to dot-call lookup when its name is one of these:
| Rule | The trait's name is |
|---|---|
| trait.avail.moduleModule | declared in the current module, or introduced by a use declaration in it |
| trait.avail.scopeScope | visible in the current lexical scope |
| trait.avail.preludePrelude | supplied by the prelude |
- trait.avail.concreteFor a concrete receiver, a trait is available to dot-call lookup when its name is one of the cases in the table above.
- trait.avail.genericFor a generic receiver, its declared bounds are also available.
- trait.avail.dynamicA dynamic trait value always exposes the methods of its own erased trait.
- trait.avail.no-injectionAn implementation in the dependency graph does not inject its trait's method names into every module that can name the target type.
- trait.avail.not-candidateA method of a trait that is not available is not a candidate at all, wherever its implementation is declared.
- trait.avail.as-absentLookup proceeds as if the implementation were absent, so a promoted method of the same name may be selected.
- trait.avail.suggestA call that finds no method should suggest a use declaration for the trait.
use std.cmp.Eq
@derive(Eq)
struct Point:
x: i32
fn same(a: Point, b: Point) -> bool:
a == b # Eq is available through the use#Inherent Methods Win
- trait.resolve.inherent-firstA visible inherent method of the receiver's nominal type is always selected over trait methods. That includes a method of a trait that the same type implements.
trait Show:
fn show(self) -> string
struct Wrapper:
value: i32
impl Wrapper:
fn show(self) -> string: "inherent"
impl Show for Wrapper:
fn show(self) -> string: "trait"
fn demo(w: Wrapper) -> string:
w.show() # the inherent method winsWhy. Only the package that owns the type can declare an inherent method, so no other package can change which method such a call reaches.
#Ambiguous Methods
- trait.resolve.ambiguousSuppose more than one available trait that the receiver implements supplies a method with that name, and no inherent method of that name is usable. The call is then an error. Error:
ambiguous-method. - trait.resolve.ambiguous.defaultsThis holds whether each method is written in its implementation or comes from a default.
- trait.resolve.no-rankingThe compiler does not select by conversion ranking or declaration order.
- trait.resolve.qualified-fixA trait-qualified call resolves the ambiguity.
trait Left:
fn label(self) -> i32
trait Right:
fn label(self) -> i32
struct User:
value: i32
impl Left for User:
fn label(self) -> i32: self.value
impl Right for User:
fn label(self) -> i32: self.value
fn main() -> i32: User { value: 42 }.label() # error: ambiguous-methodOpen in playgroundSee also: Member Resolution.
#Instantiations Of One Generic Trait
A receiver may implement one generic trait at several instantiations that
each supply the method, as with impl Add[i32] for Money and
impl Add[Money] for Money.
- trait.resolve.instantiationWhen the receiver implements one generic trait at several instantiations that each supply the method, the call chooses the instantiation.
- trait.resolve.instantiation.candidateEach instantiation is a candidate.
- trait.resolve.appliesA candidate applies when the call's arguments check against its method's parameter types, with that instantiation's trait arguments substituted.
- trait.resolve.applies.expectedWhen the call has an expected type, a candidate applies only if, in addition, the method's result type is assignable to it.
- trait.resolve.one-appliesExactly one applicable candidate is selected, so
price.add(5)calls theAdd[i32]method. - trait.resolve.literal-argSuppose two or more candidates apply only because an unsuffixed literal argument could take several widths. The literal then has its default type, by
types.literal.local.instantiation, and the candidate for that type is selected. - trait.resolve.literal-arg.exampleWith
impl Add[i32] for Moneyandimpl Add[i64] for Money,price.add(-5)calls theAdd[i32]method. - trait.resolve.literal-arg.no-applyWith the same two implementations,
price.add(5)is an error, because no candidate takesusize; write+5. Error:type-mismatch. - trait.resolve.many-applyOtherwise two or more applicable candidates are an error, and a trait-qualified call such as
Add::[i64]::add(price, 5)resolves it. Error:ambiguous-method. - trait.resolve.no-applyWhen no candidate applies, the call is an error whose message lists the available instantiations. Error:
type-mismatch. - trait.resolve.one-trait-onlyThis choice applies only among instantiations of one trait. Methods of two different traits stay ambiguous whatever the argument types. Error:
ambiguous-method.
Note. When the receiver is a literal, the call's arguments and implementations decide its width, by
types.literal.local.form.receiver. A candidate here is then an instantiation of that one type.
trait Pick[T]:
fn pick(self) -> T
struct Money:
cents: i32
impl Pick[i32] for Money:
fn pick(self) -> i32:
self.cents
impl Pick[string] for Money:
fn pick(self) -> string:
"money"
fn invalid(price: Money) -> void:
value := price.pick() # error: ambiguous-method#Trait-Qualified Calls
Select one trait explicitly with Trait::method(receiver, arguments...):
label := Display::to_string(value)
sum := Add::[Money]::add(left, right)- trait.qualified.form
Trait::method(receiver, arguments...)selects one trait explicitly. - trait.qualified.receiverThe receiver is the first ordinary argument and must implement the named trait instantiation.
- trait.qualified.argumentsRemaining arguments follow normal positional/named ordering.
- trait.qualified.bypassThis form bypasses member lookup and selects exactly the named trait method.
- trait.qualified.type-argumentsA generic trait method takes its explicit type arguments after the method name, as in
Identity::select::[i32](picker, 42). - trait.qualified.trait-argumentsThe trait's own type arguments stay before
::. - trait.qualified.generic-rulesThe type argument list follows the rules of Generic Functions.
- trait.qualified.declaring
Trait::name, as a call or a reference, names only a member thatTraititself declares. Naming a supertrait's member is an error, and the message names the declaring trait. Error:unknown-method.
trait Parent:
fn id(self) -> i32
trait Child < Parent:
fn label(self) -> string
struct Item: pass
impl Parent for Item:
fn id(self) -> i32: 1
impl Child for Item:
fn label(self) -> string: "item"
fn show(item: Item) -> i32:
first := Parent::id(item) # valid: Parent declares id
Child::id(item) # error: unknown-methodNote. A dot call still reaches a supertrait's member, as in
item.id(). The message says "declared by 'Parent'; write Parent::id".
#Associated Function Calls
An associated function is called through a type, a type parameter, or a trait:
trait Factory:
fn create() -> Self
struct User:
name: string
impl Factory for User:
fn create() -> User:
User { name: "new" }
fn make[T < Factory]() -> T:
T::create()
first := User::create()
second := make::[User]()- trait.assoc-call.type
Type::f(args)first looks for an inherent associated function or methodfofType. - trait.assoc-call.type.traitsWhen
Typehas no inherent memberf, the candidates are the members namedfof the available traits thatTypeimplements. - trait.assoc-call.type.ambiguousTwo or more trait candidates are an error, and a trait-qualified call resolves it. Error:
ambiguous-method. - trait.assoc-call.type.noneNo candidate at all is an error. Error:
unknown-method. - trait.assoc-call.parameter
T::f(args), whereTis a type parameter, resolvesfthrough the bounds ofT, and the call uses the bound's evidence. - trait.assoc-call.parameter.oneExactly one trait among the bounds of
Tand their supertraits must declaref. Two or more are an error. Error:ambiguous-method. - trait.assoc-call.parameter.static
T::f()is always resolved statically through a bound, never through a runtime type object. - trait.assoc-call.trait
Trait::f(args)for an associated functionfinfersSelflike a generic argument of the call, from the arguments and the expected type. - trait.assoc-call.trait.undeterminedA
Trait::f(args)call whoseSelfthat inference does not determine is an error; writeType::f(args)orT::f(args)instead. Error:cannot-infer-type.
See also: Trait-Qualified Calls,
Trait Availability, and
Templates, where a Structure:: call has
the template's T as its Self.
#Generic Bounds And Static Dispatch
A generic bound requires explicit conformance and uses static dispatch:
fn show[T < Display](value: T) -> string:
value.to_string()Bounds compose with &:
fn audit[T < Display & Named](value: T) -> string:
value.to_string() + " / " + value.name()- trait.bound.staticA generic bound requires explicit conformance and uses static dispatch.
- trait.bound.compose-andBounds compose with
&: the type must implement every trait joined by it. - trait.bound.repeatA bound may list the same trait more than once, as in
T < Display & Display. The repetition adds no requirement and is not diagnosed. - trait.bound.supertraitsA bound implies a bound on each of its trait's transitive supertraits. Inside
fn has_child[T < Child], aTvalue may be passed whereT < Parentis required. - trait.bound.unsatisfiedA type argument, explicit or inferred, that does not implement a trait its parameter's bound requires is an error. Error:
unsatisfied-trait-bound. - trait.bound.unsatisfied.casesThis includes a non-reference type for
T < AnyRefand a reference type forT < AnyVal. - trait.bound.depthA bound required directly by a use has depth 1. A bound of the implementation that proves a bound of depth
nhas depthn + 1. - trait.bound.depth.limitA proof that needs a bound of depth greater than 64 is an error, whether or not a deeper proof would succeed. Error:
trait-resolution-depth. - trait.bound.depth.fixedThe limit is fixed by this specification; no package, module, or compiler option changes it.
- trait.bound.representationThe compiler may monomorphize static calls, share one body among instantiations, or use another representation.
- trait.bound.representation.semanticsThe chosen representation must preserve the observable semantics.
trait Parent:
fn parent(self) -> i32
trait Child < Parent:
fn child(self) -> i32
fn has_parent[T < Parent](value: T) -> i32:
value.parent()
fn has_child[T < Child](value: T) -> i32:
has_parent(value) + value.child()Why. A fixed limit makes every implementation accept the same programs, and it ends bound solving that would otherwise never finish.
See also: the non-normative Implementation Model.
#Declared Bounds Are Not Implied
A generic type's declared bounds are never implied where the type is
written. Map is declared as Map[K < Eq & Hash, V], so each declaration
that writes Map[K, V] bounds its own K:
fn stock_of[K < Eq & Hash, V](stock: Map[K, V], item: K) -> V?:
stock.get(item)
struct Index[K < Eq & Hash, V]:
name: string
entries: Map[K, V]
impl[K < Eq & Hash, V] Index[K, V]:
fn lookup(self, key: K) -> V?:
self.entries.get(key)- trait.bound.no-impliedA generic type's declared bounds are never implied where the type is written.
- trait.bound.no-implied.positionsThis holds wherever a type is written, including an impl header's target and trait arguments, a function or method signature, a field, and a bound.
- trait.bound.no-implied.enclosingA declaration whose generic parameter
Tappears in a writtenC[T]must itself giveTevery bound thatC's parameter requires. Error:unsatisfied-trait-bound.
So impl[K, V] Iterable[(K, V)] for Map[K, V] and
fn count[K, V](m: Map[K, V]) -> usize are errors. Writing the bound fixes
each, as in impl[K < Eq & Hash, V] Iterable[(K, V)] for Map[K, V].
trait Catalog:
fn catalog_name(self) -> string
impl[K, V] Catalog for Map[K, V]: # error: unsatisfied-trait-bound
fn catalog_name(self) -> string:
"inventory"
fn stock_report[K, V](stock: Map[K, V], title: string) -> string: # error: unsatisfied-trait-bound
"Stock report: " + title
struct Index[K, V]:
name: string
entries: Map[K, V] # error: unsatisfied-trait-boundNote. The bounds a declaration writes are ordinary bounds, so its bodies may use them, as
lookupcallsgetabove. There is no separate implied-bound mechanism.
Why. Every requirement is visible in the declaration that relies on it, as in Rust. A bound written once at the type would otherwise leak silently into every impl and signature that names the type.
See also: trait.bound.unsatisfied,
trait.avail.generic, and
types.map-key.declared-bound.
#Associated Type Bindings
A trait in a generic parameter bound may bind associated types after its positional type arguments:
trait Supplier:
type Item
fn get(self) -> Self::Item
fn describe[T < Display, I < Supplier[Item = T]](source: I) -> string:
source.get().to_string()
struct Feed[I]:
source: I
impl[T < Display, I < Supplier[Item = T]] Display for Feed[I]:
fn to_string(self) -> string:
describe(self.source)- trait.binding.formA trait in a generic parameter bound may bind associated types after its positional type arguments.
- trait.binding.meaning
I < Supplier[Item = T]means thatIimplementsSupplierand that its associated typeItemequalsT. - trait.binding.equalityThe binding is an equality constraint on the projection
I::Item, not a new type. - trait.binding.insideInside the declaration,
I::Itemremains a valid projection and denotes the same type asT. - trait.binding.interchangeableThe two spellings are interchangeable in parameter, result, and body types.
- trait.binding.use-siteAt a use site, after substitution, the argument's implementation of the trait must bind the associated type to the bound type.
- trait.binding.inferenceThe constraint participates in generic argument inference, so
Tabove is inferred from theSupplierimplementation of the argument passed forI. - trait.binding.mismatchAn argument whose implementation binds a different type is an error. Error:
unsatisfied-trait-bound. - trait.binding.reachableA binding counts as a bound reachable from its parameter.
- trait.binding.reachable.exampleIn the implementation above,
Tis therefore constrained throughI. The implementation satisfies the rule that every generic implementation parameter be constrained.
#Binding Names
A binding may name an associated type that a supertrait declares:
trait Supplier:
type Item
fn get(self) -> Self::Item
trait NamedSupplier < Supplier:
fn name(self) -> string
fn first[T, I < NamedSupplier[Item = T]](source: I) -> T:
source.get()- trait.binding.scopeThe bound type may name any parameter of the same generic parameter list.
- trait.binding.name-reachA binding name must be an associated type that the named trait declares or reaches through its supertraits, as
NamedSupplierreachesSupplier'sItem. Naming anything else is an error. Error:unknown-associated-type. - trait.binding.name-reach.meaningA binding of a supertrait's associated type constrains that supertrait's projection, so
I::Itemabove equalsT. - trait.binding.name-reach.ambiguous-typeWhen two different associated type declarations reachable that way have the name, the binding is ambiguous. An ambiguous binding is an error. Error:
ambiguous-associated-type. - trait.binding.onceEach projection may be bound at most once in one generic parameter list.
- trait.binding.once.errorA second binding of the same parameter's associated type, in the same bound or another bound, is an error. This holds even when both bindings name the same type. Error:
duplicate-associated-binding. - trait.binding.once.elaboratedThese two rules count written bindings only. A binding that arrives through a supertrait follows
trait.binding.super.mergeinstead.
trait Supplier:
type Item
fn get(self) -> Self::Item
fn first[T, I < Supplier[Element = T]](source: I) -> T: # error: unknown-associated-type
source.get()
fn second[T, I < Supplier[Item = T, Item = T]](source: I) -> T: # error: duplicate-associated-binding
source.get()
trait Named:
type Item
trait Keyed:
type Item
trait Record < Named & Keyed
fn third[T, R < Record[Item = T]](record: R) -> T: # error: ambiguous-associated-type
panic("unreachable")Why. As in Rust, a subtrait's bound can fix a supertrait's associated type without a second bound. Only a name that two supertraits declare separately needs the longer spelling.
#Binding Positions
- trait.binding.positions-keyBindings appear in generic parameter bounds, in supertrait lists, in trait value types, and in requirement keys.
- trait.binding.rejected-trait-typeThe trait of an
implheader, a trait-qualified call, and a method reference do not accept them. The grammar reports an error there. Error:syntax-error. - trait.binding.non-traitA binding in the arguments of a named type that is not a trait, as in
List[i32, Item = i32], is an error. Error:unknown-associated-type. - trait.binding.ambiguous-projectionA binding does not make an ambiguous projection unambiguous. When two bounds on
Iboth declareItem,I::Itemis still ambiguous even if one of them binds it. Error:ambiguous-associated-type.
trait Source:
type Item
fn get(self) -> Self::Item
fn demo[S < Source[Item = i32]](s: S) -> i32:
s.get()#Supertrait Bindings
A supertrait may bind an associated type of its trait, so every implementation fixes it:
use std.ops.Add
trait Summable < Add[Out = Self]
fn double[T < Summable](value: T) -> T:
value + value- trait.binding.super.formA trait in a supertrait list may bind associated types after its positional arguments, as a bound does.
- trait.binding.super.meaning
trait C < S[A, Out = U]requires that a typeXimplementingCimplementS[A]withOutequal toU, whereAandUreadXforSelf. - trait.binding.super.projectionFor a type parameter
T < C, and forSelfinsideC, the projection is known to equalU. Sovalue + valueabove has typeT. - trait.binding.super.mismatchAn
impl C for Xwhose implementation ofS[A]bindsOutto another type is an error. Error:missing-supertrait-implementation. - trait.binding.super.namesThe rules of Binding Names apply to a supertrait list as to a generic parameter list.
- trait.binding.super.mergeWhen two supertrait paths of a trait bind the same associated type to the same type, the bindings merge into one.
- trait.binding.super.conflictWhen the two paths bind it to different types, the trait declaration is an error. The diagnostic is reported at the declaring trait and names both paths. Error:
duplicate-associated-binding. - trait.binding.super.conflict.boundThe bounds of one generic parameter follow the same two rules, as in
T < Numbers & Words. A conflict there is reported on that parameter's bound list. A binding written in one bound that names a different type from the one the bound's own supertraits fix is the same conflict, as inT < Prices[Item = string]undertrait Prices < Source[Item = i32]. Error:duplicate-associated-binding.
trait Source:
type Item
fn get(self) -> Self::Item
trait Counts < Source[Item = i32]:
fn count(self) -> i32
trait Sums < Source[Item = i32]:
fn sum(self) -> i32
trait Stats < Counts & Sums # valid: both paths bind Item = i32
trait Numbers < Source[Item = i32]
trait Words < Source[Item = string]
trait Both < Numbers & Words # error: duplicate-associated-bindingWhy. Equal bindings say the same thing twice, as a diamond often does. Different ones would make
i32andstringequal inside every body that names the trait.
use std.ops.Add
trait Summable < Add[Out = Self]
struct Money:
cents: i64
impl Add for Money:
type Out = i64
fn add(self, rhs: Money) -> i64:
self.cents + rhs.cents
impl Summable for Money # error: missing-supertrait-implementation#Dynamic Trait Values
A trait value is a shared or mutable reference to an object with dynamic
dispatch, written &dyn Tr or &mut dyn Tr:
fn print_display(value: &dyn Display) -> void $ Console:
println(value.to_string())- trait.dyn.reference-formA trait value type is written
&dyn Tr,&mut dyn Tr,*dyn Tr, or*mut dyn Tr, for a traitTr. - trait.dyn.reference-contentsA trait value reference refers to a concrete object and carries dispatch metadata for the trait.
- trait.dyn.reference-keywordA bare trait name in a type position is an error: its fix-it inserts
dynafter an existing&or&mut, and writes&dynotherwise. Error:trait-used-as-type. - trait.dyn.bare-value-rejectedA written
dyn Tris invalid as a value type, including within a container or alias. Error:trait-used-as-type. - trait.dyn.bare-value-no-fixThe bare trait value error has no fix-it.
- trait.dyn.referent-descriptorThe descriptor
dyn Tris valid as the immediate referent of&,&mut,*, or*mut, optionally grouped, or as an inherent implementation target. - trait.dyn.reference-no-bare-readExplicitly dereferencing a trait value reference would produce an unsized bare value and is an error. Error:
trait-used-as-type. - trait.dyn.keyword.positionsThe type positions include parameter, result, field, payload, and local annotation types, type arguments, and associated type bindings.
- trait.dyn.keyword.bareA trait stays bare where a trait, not a type, is named: a bound, a supertrait list, an implementation header's trait, a requirement key, and a trait-qualified call.
- trait.dyn.value-methodsThe available methods of the trait and of its transitive supertraits, and the methods of an inherent
impl dynblock, may be called through the value. The concrete type's inherent methods may not.
trait Greet:
fn greet(self) -> string
fn demo(g: Greet) -> string: # error: trait-used-as-type
g.greet()Why.
&dynmakes the reference and runtime dispatch explicit. A bound such asT < Greetstays fully monomorphized.
#Dynamic Safety
- trait.dyn.any-traitEvery trait may be used as a
dyntype. No rule about a trait's members makes itsdyntype invalid. - trait.dyn.member.unavailableA member of the trait or of a supertrait that cannot work dynamically is unavailable on the
dynvalue. - trait.dyn.member.unavailable.errorCalling an unavailable member through a
dynvalue is an error, reported at the call. Error:dyn-member-unavailable.
Each kind of member is available or unavailable on a dyn value:
| Rule | Form | On a dyn value |
|---|---|---|
| trait.dyn.member.assoc-functionAssociated functions | an associated function, which has no self receiver |
unavailable |
trait.dyn.member.selfSelf |
a method that names Self anywhere other than its receiver |
unavailable |
| trait.dyn.member.opaque-resultOpaque results | a method whose return annotation contains impl Trait |
unavailable |
| trait.dyn.safe.assoc-type.reference-valuesAssociated types | a method whose signature names an associated type | available; the dyn type binds every associated type, as &dyn Supplier[Item = i32] does |
| trait.dyn.safe.method-type-paramMethod type parameters | a method-level type parameter, whatever its bounds, as in T < Display or T < Error |
available; a call through the trait value may pass any type argument, a value type included |
| trait.dyn.safe.suspendingSuspending methods | a suspending method, as the prelude Console's write! is |
available |
| trait.dyn.safe.row-parameterRow parameters | a method-level row parameter | available |
- trait.dyn.member.other-with-opaqueEvery method outside the unavailable categories above is available.
- trait.dyn.member.type-param-useA method type parameter may be used in any way in the method's signature and body, including inside a container or a function type.
- trait.dyn.generic-traitGeneric parameters of the trait itself are allowed when the value type names one complete instantiation.
- trait.dyn.generic-trait.self-defaultA trait value type that omits a parameter whose default names
Selfis an error, since noSelfis known there. Error:partial-generic-arguments. - trait.dyn.opaque-result.no-slotAn opaque-result method has no dynamic dispatch table slot.
- trait.dyn.opaque-result.hintA diagnostic for calling an opaque-result method through a trait value reference suggests using a generic bound.
trait Runner:
fn run[$R](self, job: fn() -> void $ R) -> void $ R
fn valid(runner: &dyn Runner) -> void:
passA method type parameter bounded by Error, whose argument may be an enum,
keeps the method available:
use std.error.Error
trait Registry:
fn lookup[T < Error](self, name: string) -> T?
fn valid(registry: &dyn Registry) -> void:
passA method that takes Self is unavailable, while the rest of the trait
works:
trait Versioned:
fn version(self) -> i32
fn newer_than(self, other: Self) -> bool
fn audit(record: &dyn Versioned) -> bool:
if record.version() > 0:
return true
record.newer_than(record) # error: dyn-member-unavailable
trait Source:
fn items(&self) -> impl Iterator[i32]
fn unavailable(source: &dyn Source) -> void:
source.items() # error: dyn-member-unavailableWhy. A member is judged where it is called, as in Swift 5.7, so one unusual member never blocks a whole trait. An associated function has no receiver to dispatch on. A
Selfparameter needs the concrete type, which thedynvalue has erased. A bound associated type makes every signature that uses it concrete.
See also: Trait Values And Any.
#Bound Associated Types
A trait value type binds the trait's associated types as a bound does:
trait Supplier:
type Item
fn get(self) -> Self::Item
struct Constant:
value: i32
impl Supplier for Constant:
type Item = i32
fn get(self) -> i32: self.value
fn read(source: &dyn Supplier[Item = i32]) -> i32:
source.get() + 1
fn first[T < Supplier](source: T) -> T::Item:
source.get()
fn run() -> i32:
let source: &dyn Supplier[Item = i32] = &Constant { value: 41 }
first(source) + read(source)- trait.dyn.binding.form.reference-valuesA trait value type may bind associated types after its positional arguments, as in
&dyn Supplier[Item = i32]. - trait.dyn.binding.complete.reference-valuesIt must bind every associated type of the trait and of its supertraits. A binding that a supertrait list fixes counts as bound, so under
trait Sub < Super[Item = T]the types&dyn Suband&dyn Sub[Item = T]are one type. A trait value type that leaves one unbound is an error. Error:trait-not-dynamically-safe. - trait.dyn.binding.self-fixedA binding to
Selfthat a supertrait list fixes, as intrait Summable < Add[Out = Self], counts as bound. The trait value does not carry it, soOutstays a projection. - trait.dyn.binding.namesThe rules of Binding Names apply, so a binding may name a supertrait's associated type. An unknown name is an error. Error:
unknown-associated-type. An ambiguous name is an error. Error:ambiguous-associated-type. - trait.dyn.binding.signaturesThrough the value, each projection in a method signature denotes its bound type, so
getabove returnsi32. - trait.dyn.binding.reference-convertA concrete reference converts to a trait value reference only when its referent's implementation binds each associated type to the bound type. Any other such conversion is an error. Error:
type-mismatch. - trait.dyn.binding.identityTwo trait value types are the same type when they name the same trait instantiation and bind each associated type to the same type. Order does not matter.
- trait.dyn.binding.widen.reference-valuesWidening to a supertrait value keeps the bindings of the associated types that the supertrait reaches, so
&dyn NamedSupplier[Item = i32]widens to&dyn Supplier[Item = i32].
trait Supplier:
type Item
fn get(self) -> Self::Item
trait Factory:
type Item
fn count() -> i32
fn unbound(source: &dyn Supplier) -> void: # error: trait-not-dynamically-safe
pass
fn total[F < Factory[Item = i32]]() -> i32:
F::count()
fn with_function(factory: &dyn Factory[Item = i32]) -> i32:
total::[*dyn Factory[Item = i32]]() # error: unsatisfied-trait-boundWhy. Rust accepts
&dyn Iterator<Item = T>, and hd had simply not added the form. Once every associated type is bound, every method signature is concrete. An associated function still has no receiver to dispatch on, so it stays unavailable whatever the type binds.
#Supertrait Widening
- trait.dyn.supertrait-methodsA child-trait bound or dynamic value exposes the methods of its transitive supertraits.
- trait.dyn.widenA dynamic child-trait value widens implicitly to a supertrait value, losing access to child-only methods.
- trait.dyn.no-narrowNo conversion reverses the widening, so converting back is an error. Error:
type-mismatch. - trait.dyn.recoverOnly a value of
Inspectableor of a trait that extends it can recover its concrete type, through Runtime Type Identity.
trait Animal:
fn name(self) -> string
trait Dog < Animal:
fn bark(self) -> string
struct Pup:
name: string
impl Animal for Pup:
fn name(self) -> string: self.name
impl Dog for Pup:
fn bark(self) -> string: "woof"
fn name_of(animal: &dyn Animal) -> string:
animal.name()
fn demo(pup: Pup) -> string:
name_of(&pup) # the reference converts to Animal#Trait Values As Bounds
trait Named < Display:
fn name(self) -> string
fn show[T < Display](value: T) -> string:
value.to_string()
fn tag[T < Named](value: T) -> string:
value.name()
fn describe(named: &dyn Named, shown: &dyn Display) -> string:
tag(named) + show(named) + show(shown)- trait.dyn.bound.referenceA trait value reference satisfies bounds on its trait and supertraits when every required member is available through that reference.
- trait.dyn.bound.reference-sharedA shared reference provides
selfand&selfmembers; a&mut selfmember requires a mutable trait value reference. - trait.dyn.bound.reference-projectionProjections through such a bound use the associated types bound by the referenced trait value.
- trait.dyn.bound.reference-bindingA bound's associated-type bindings and trait arguments must match the referenced trait value's bindings and arguments. Error:
unsatisfied-trait-bound. - trait.dyn.bound.reference-dispatchCalls through such a bound dispatch through the referenced trait value's table.
- trait.dyn.bound.reference-no-implThis adds no implementation and permits no trait value reference as an implementation target.
Why. Generic code under
T < Trmay call any member ofTr, including an associated function such asT::create(). The availability condition, with the bound associated types, keeps every such call meaningful for adynargument.
#Conversion To Trait Values
- trait.dyn.convert.reference-valuesConverting a concrete reference to a trait value reference requires an implementation of the trait for its referent type. A conversion without one is an error. Error:
type-mismatch. - trait.dyn.convert.any-typeThe concrete type can be composite or primitive.
- trait.dyn.type-testA dynamic trait value supports a type test only when its trait is
Inspectableor extends it. The test recovers an exact concrete type. - trait.dyn.no-trait-testNo test asks whether a value implements another trait.
trait Greet:
fn greet(self) -> string
struct User:
name: string
impl Greet for User:
fn greet(self) -> string: "hi"
fn demo(user: User) -> *dyn Greet:
&user # the reference converts through the explicit implementationSee also: Runtime Type Identity.
#Trait Value References
A trait value reference, written &dyn Tr or &mut dyn Tr, is a trait
value that is a reference: it refers to one object whose type implements
Tr. It is made only from a reference the program writes or already
holds, as in Rust: &s, &mut s, &S { ... }, or a &S value. Bare dyn Tr is not a value type.
trait Counter:
fn bump(&mut self) -> void
fn total(&self) -> i32
struct Tally:
n: i32
impl Counter for Tally:
fn bump(&mut self) -> void:
self.n += 1
fn total(&self) -> i32:
self.n
fn demo() -> i32:
let mut tally = Tally { n: 0 }
let shared: &mut dyn Counter = &mut tally
shared.bump() # changes `tally`
let fresh: &dyn Counter = &Tally { n: 5 } # a new object
tally.bump()
tally.n + fresh.total() # 2 + 5- trait.dyn.ref.form
&dyn Trand&mut dyn Trare trait value references. They are reference types, as Reference Types defines. - trait.dyn.ref.share
&Sconverts implicitly to&dyn Tr, and&mut Sto&mut dyn Tr, whenSimplementsTr. The result refers to the place the source refers to. - trait.dyn.ref.writtenA value that is no reference never converts to
&dyn Tror&mut dyn Tr. Using one where a trait value reference is expected is an error, so a new object is written&eor&mut e, as in&S { ... }. Error:type-mismatch. - trait.dyn.ref.written.reference-fix.reference-valuesThe error's fix-it writes
&before the value, or&mutwhere&mut dyn Tris expected, for a place or struct literal whose type implementsTr. No fix-it offers&muton a readonly view. - trait.dyn.ref.convert.implEither conversion is an error when
Sdoes not implementTrexplicitly. Error:type-mismatch. - trait.dyn.ref.weaken
&mut dyn Trconverts implicitly to&dyn Tr, and both refer to the same object. - trait.dyn.ref.no-upgrade
&dyn Trand&Snever convert to&mut dyn Tr. Error:mutable-upgrade. - trait.dyn.ref.widenA child trait value reference converts implicitly to a supertrait value reference of the same mutability, which refers to the same object.
- trait.dyn.ref.memberThrough
&dyn Tr, the methods declaredselfand&selfare available. Through&mut dyn Tr, the methods declaredmut selfand&mut selfare available too. - trait.dyn.ref.member.mutCalling a
mut selfor&mut selfmethod through&dyn Tris an error. Its fix-it writes&mut dynin the declared type of the receiver binding. Error:not-writable. - trait.dyn.ref.member.other.reference-valuesOther members follow Dynamic Safety.
- trait.dyn.ref.write-throughA method that changes its receiver, called through a trait value reference, changes the object the reference refers to. Through a reference converted from
&mut s, that object issitself. - trait.dyn.ref.identity
ison two trait value references compares the places they refer to, byref.identity.same-place. Two conversions of one&srefer to one place, and two conversions of&eon a value refer to two new objects. - trait.dyn.ref.no-store
*r = vand*r op= vwith a trait value referencerare errors, sincerdoes not know the type of what it refers to. Error:type-mismatch. - trait.dyn.ref.counted
*dyn Trand*mut dyn Trare counted trait value references. Each rule of this section holds for them with*in place of&on both sides of a conversion, so*mut Sconverts to*mut dyn Tr. - trait.dyn.ref.counted.lendA counted trait value reference lends itself as a local one, as
ref.convert.lendstates:*dyn Trconverts to&dyn Tr. - trait.dyn.ref.counted.memberThrough
*dyn Tr, the methods declared*selfare available too, and through*mut dyn Tralso those declared*mut self.
trait Counter:
fn bump(&mut self) -> void
fn poke(counter: &dyn Counter) -> void:
counter.bump() # error: not-writable
fn upgrade(counter: *dyn Counter) -> *mut dyn Counter:
counter # error: mutable-upgrade
struct Tally:
n: i32
impl Counter for Tally:
fn bump(&mut self) -> void:
self.n += 1
fn count(counter: &mut dyn Counter) -> void:
counter.bump()
fn demo() -> i32:
let mut tally = Tally { n: 0 }
count(tally) # error: type-mismatch; the fix-it writes `count(&mut tally)`
tally.nWhy. A trait value reference is how a callee changes what the caller holds. A conversion that made a new object for a value would drop the callee's writes, so the program writes the
&that says which object it means, as Rust requires. Writing&mutsays that methods called through it may change the object. A method call's receiver still takes its address, byfn.method.auto-address.
See also: Values And References, Reference Receivers.
#Any
Any is the universal empty trait.
- trait.any.universal
Anyis the universal empty trait. - trait.any.allEvery value type, including an optional type, implements
Anyautomatically. - trait.any.never
neverimplementsAnyas well, bytypes.any.never. - trait.any.erases.reference-valuesThe reference type
&dyn Anyhides its referent's concrete type and exposes no type-specific methods. - trait.any.optional.reference-valuesA reference to an optional value erases to
&dyn Anylike a reference to any other enum value. - trait.any.none.reference-valuesA bare
.Nonestill needs an expected optional type:let value: &dyn Any = &.Noneis an error, whilelet value: (&dyn Any)? = .Noneis valid. Error:missing-contextual-enum-type.
let invalid: *dyn Any = &.None # error: missing-contextual-enum-type#Sealed Traits
A sealed trait is a standard trait whose implementations only the compiler and the standard library supply.
| Rule | Trait | Implemented for |
|---|---|---|
| trait.sealed.anyAny | Any |
every value type (Any) |
| trait.sealed.anyval-membersAnyVal | AnyVal |
the primitive types, string, tuples (void included), enums (optionals and Result included), structs, List, Map, function types, and newtypes over them (Trait Values And Any) |
| trait.sealed.anyref-membersAnyRef | AnyRef |
the reference types, trait value references included, and the types whose values are shared objects, which lists and maps are not (Trait Values And Any) |
| trait.sealed.inspectableInspectable | Inspectable |
the inspectable types (Inspectable Types) |
| trait.sealed.structureStructure | Structure |
each derivation's target, while its template is instantiated (The Structure Trait) |
| trait.sealed.numNum | Num |
every integer and floating-point type (Numeric Traits) |
| trait.sealed.integerInteger | Integer |
i8, i16, i32, i64, u8, u16, u32, u64, and usize (Numeric Traits) |
| trait.sealed.floatFloat | Float |
f32 and f64 (Numeric Traits) |
| trait.sealed.tupleTuple | Tuple |
every tuple type (Function Type Constructors) |
- trait.sealed.definitionA sealed trait is a standard trait whose implementations only the compiler and the standard library supply.
- trait.sealed.listThe sealed traits are those in the table above.
- trait.sealed.use-positions-structureUser code may name a sealed trait in a bound, as a supertrait, and, where the trait is dynamically safe, as a value type.
Structureis the exception: it may be named only whereannot.structure.named-positionsallows. - trait.sealed.no-user-implUser code cannot implement a sealed trait.
- trait.sealed.user-implAn
implof a sealed trait outside the standard library is an error, reported on theimplline, whatever its target. Error:sealed-trait-implementation.
use std.inspect.{Inspectable, TypeId}
struct User:
name: string
struct Handle: pass
impl Inspectable for User: # error: sealed-trait-implementation
fn runtime_type(self) -> TypeId: TypeId::of::[string]()
impl AnyRef for Handle # error: sealed-trait-implementation#Compiler-Supplied Implementations
- trait.sealed.suppliedThe compiler supplies the implementations listed for each sealed trait.
- trait.sealed.behavesA compiler-supplied implementation behaves like an explicit one: it satisfies bounds, constructs dynamic trait values, and counts for supertrait checks.
- trait.sealed.no-replaceA compiler-supplied implementation cannot be replaced or overridden.
- trait.sealed.member-namesA trait may have a sealed trait as a direct or transitive supertrait. Such a trait must not declare a member with the name of one of that sealed trait's members.
- trait.sealed.member-names.implAn implementation of such a trait must not write such a member either.
- trait.sealed.member-names.errorEither is an error, reported on the member, in place of
duplicate-trait-member. Error:sealed-trait-implementation. - trait.sealed.inherent-okAn inherent method with the same name is allowed.
- trait.sealed.inherent-lookupOrdinary method lookup finds that inherent method on the concrete type, and it changes nothing that the compiler-supplied implementation reports.
use std.inspect.Inspectable
struct User:
name: string
fn id_of(value: User) -> string:
"${value.runtime_type()}" # no impl Inspectable for User written: the compiler supplies it#Extending A Sealed Trait
- trait.sealed.extendA trait that extends a sealed trait is declared and implemented normally.
- trait.sealed.extend.membersIts implementation writes only the child's members; the sealed supertrait's implementation comes from the compiler.
- trait.sealed.extend.missingWhen the target is not a type the compiler supplies the sealed trait for, the implementation is an error, as for any missing supertrait. Error:
missing-supertrait-implementation.
use std.inspect.Inspectable
trait Named < Inspectable:
fn type_name(self) -> string
struct User:
name: string
impl Named for User:
fn type_name(self) -> string: "User"#Numeric Traits
std.num declares three sealed traits for the primitive number types, so
generic code can compute over any of them:
use std.num.Num
fn sum[T < Num](items: List[T]) -> T:
let total = T::zero()
for item in items:
total = total + item
total
fn kilo[N < Num](n: N) -> N:
n * N::from_i64(1000)Num is declared in this shape:
pub trait Num < AnyVal & PartialOrd & Display & Add[Out = Self] & Sub[Out = Self] & Mul[Out = Self] & Div[Out = Self] & Rem[Out = Self]:
fn zero() -> Self
fn one() -> Self
fn from_i64(n: i64) -> Self| Rule | Trait | Supertraits |
|---|---|---|
| trait.num.num-orderedNum | Num |
AnyVal, PartialOrd (and so Eq), Display, and Add, Sub, Mul, Div, and Rem, each as [Out = Self] |
| trait.num.integerInteger | Integer |
Num, Ord, BitAnd, BitOr, and BitXor, each as [Out = Self], Not[Out = Self], Shl[u32, Out = Self], and Shr[u32, Out = Self] |
| trait.num.floatFloat | Float |
Num, PartialOrd, and Neg[Out = Self] |
- trait.num.module
std.numdeclaresNum,Integer, andFloat, with the supertraits in the table above. - trait.num.sealedThe three traits are sealed. They stand for the built-in primitive number types only, and only the standard library implements them.
- trait.num.not-newtypesA newtype over a number, such as
type Meters(i64), and a library number type, such as aBigInt, are notNum. They implement the operator traits they need by hand. - trait.num.members.std
Numdeclareszero,one, andfrom_i64. The other numeric methods, such aschecked_addandis_nan, are stdlib-tier inherent methods of the number types. - trait.num.zero-one
T::zero()andT::one()are the values 0 and 1 ofT. A numeric literal never has a type parameter's type, so generic code builds constants from these functions. - trait.num.from-i64-checked
T::from_i64(n)is checked in every build. For an integer typeT, it returnsnas aTwhenTcan hold it, and otherwise panics withinteger-overflow. A floating-point type takes the nearest value. - trait.num.from-i64-castA numeric cast
T(n)stays the way to wrap. - trait.num.division
/and%keep each type's own meaning underNum. Integer division truncates and panics on a zero divisor, and floating-point division follows IEEE 754. - trait.num.std-operatorsThe standard library's operator implementations behave as the built-in operators do.
use std.num.Num
type Meters(i64)
fn sum[T < Num](items: List[T]) -> T:
let total = T::zero()
for item in items:
total = total + item
total
fn total(items: List[Meters]) -> Meters:
sum(items) # error: unsatisfied-trait-boundA library number type implements the operator and comparison traits
directly, never Num:
use std.num.Num
use std.ops.Add
struct Cents:
value: i64
impl Add for Cents:
type Out = Cents
fn add(self, rhs: Cents) -> Cents:
Cents { value: self.value + rhs.value }
impl Eq for Cents:
fn eq(self, other: Cents) -> bool:
self.value == other.value
impl Num for Cents: # error: sealed-trait-implementation
fn zero() -> Cents: Cents { value: 0 }
fn one() -> Cents: Cents { value: 1 }
fn from_i64(n: i64) -> Cents: Cents { value: n }Note. The stdlib chapter Num specifies those methods, such as
checked_addon every integer type andis_nanonf64.
Why. Sealing keeps the families to types whose operators the compiler knows.
zeroandonereplace polymorphic literals, the simpler model of Rust'snum-traitsrather than Haskell'sfromInteger.
See also: Operator Traits, Supertrait Bindings, and the stdlib chapter Num.
#Runtime Type Identity
The standard module std.inspect lets a program erase a value so that its
concrete type can be recovered later:
trait Inspectable:
fn runtime_type(self) -> TypeId
impl dyn Inspectable:
pub fn downcast_ref[T < Inspectable](&self) -> (*T)?
pub fn downcast_mut[T < Inspectable](&mut self) -> (*mut T)?
impl TypeId:
pub fn of[T < Inspectable]() -> TypeId- trait.rtti.moduleThe standard module
std.inspectlets a program erase a value so that its concrete type can be recovered later. - trait.rtti.reference-importsIts names are not prelude names; code imports them, as in
use std.inspect.{Inspectable, TypeId}. - trait.rtti.reference-surfaceThe block above lists the public surface.
- trait.rtti.reference-bodiesThe compiler supplies the bodies of
downcast_refanddowncast_mut.
#Inspectable And TypeId
- trait.inspect.sealed
Inspectableis a sealed trait. - trait.inspect.reference-safe
Inspectableis dynamically safe; its only trait member isruntime_type. - trait.inspect.suppliedThe compiler supplies its implementation for every inspectable type.
- trait.inspect.supplied.reference-methodsThe implementation provides
runtime_type; inherent recovery methods add no vtable requirement. - trait.inspect.runtime-type
value.runtime_type()returns theTypeIdof the value's recorded type. - trait.inspect.staticFor a value whose static type is concrete, the recorded type is its static type.
- trait.inspect.dynamicFor a dynamic value of
Inspectable, it is the concrete type recorded when the value was erased, never the trait. The same holds for a dynamic value of a trait that hasInspectableas a supertrait. - trait.inspectable.referenceA reference type is inspectable when its referent is inspectable or is a trait value type.
- trait.inspect.reference-recordedFor a trait value reference,
runtime_type()returns the runtime identity of the concrete referent whose reference was erased.
use std.inspect.Inspectable
fn id_of[T < Inspectable](value: T) -> string:
"${value.runtime_type()}"See also: Trait Values And Any.
#TypeId Values
- trait.typeid.opaque-struct
TypeIdis an opaque struct, a value that copies as any struct does. User code cannot construct one or read its fields. - trait.typeid.traits
TypeIdimplementsEq,Hash, andDisplay. - trait.typeid.of
TypeIdhas the associated functionTypeId::of::[T](), which returns theTypeIdofT. - trait.typeid.equalityEquality holds exactly when two
TypeIdvalues denote the same runtime identity, as defined below. - trait.typeid.no-ops
TypeIdhas no other operations. It exposes no type arguments, fields, or shape. - trait.typeid.no-trait-queryA
TypeIdcannot answer whether a type implements a trait, and nothing can be constructed or called through it.
use std.inspect.TypeId
fn same_type() -> bool:
TypeId::of::[i32]() == TypeId::of::[i32]()#Runtime Identity
- trait.identity.definitionTwo types have the same runtime identity when they are the same declaration applied to type arguments that have the same runtime identity.
- trait.identity.normalizeRuntime identity is compared after transparent aliases are expanded.
- trait.identity.declarationsFor this rule the primitive types,
string,List,Map, each tuple arity, andAnycount as declarations, andvoidis the tuple of arity zero. - trait.identity.trait-valueA trait value type is its trait declaration applied to its arguments.
The rules have these consequences:
| Rule | Types | Runtime identity |
|---|---|---|
| trait.identity.aliasAlias | a transparent alias and its target | the same |
| trait.identity.newtypeNewtype | a newtype and its base type | different |
| trait.identity.reference-permissionReference permission | &User and &mut User |
different |
| trait.identity.argumentsArguments | Box[User] and Box[Post] |
different |
| trait.identity.no-conversion.reference-valuesNo conversion | i32 and i64; User and User?; List[FsError] and List[*dyn Error] |
different: no numeric conversion, optional injection, or variance applies |
| trait.identity.modulesModules | two declarations named User in different modules |
different |
See also: Transparent Aliases And Newtypes.
use std.inspect.TypeId
fn demo() -> bool:
TypeId::of::[List[i32]]() == TypeId::of::[List[i32]]()#Stability And Printing
- trait.typeid.buildA
TypeIddepends only on which compiled program runs. - trait.typeid.stableIts equality, hash, and printable name are therefore the same in every program instance, process, and run of one build.
- trait.typeid.cross-buildThey carry no promise across builds.
- trait.typeid.boundaryA
TypeIdis not boundary-safe. - trait.typeid.nameThe printable name, produced by
Display, spells the type canonically, as the table below defines. - trait.typeid.name.no-parseThe name is for people and logs; nothing parses it back into a type.
| Rule | Type | Printable name |
|---|---|---|
| trait.typeid.name.preludePrelude name | a prelude name | written as it is, as in i32, string, List[string], or Map[string, i32] |
| trait.typeid.name.qualifiedOther declaration | every other nominal declaration, including the trait of a trait value type | its absolute qualified name, as in std.error.Error |
| trait.typeid.name.packagePackage declaration | a declaration in a package | the package name from hd.toml with each - written _, then the module path and the name, as in acme_shop.model.User |
| trait.typeid.name.single-fileSingle-file declaration | a declaration in a single-file program | the file's stem, then the name, as in shop.User in shop.hd |
| trait.typeid.name.referenceReference | &T or &mut T |
& or &mut followed by the canonical name of T |
| trait.typeid.name.counted-referenceCounted reference | *T or *mut T |
* or *mut followed by the canonical name of T |
| trait.typeid.name.optionOption | Option[T] |
T? |
| trait.typeid.name.tupleTuple | a tuple | (A, B); the empty tuple, void included, is () |
| trait.typeid.name.separatorSeparator | elements and type arguments | separated by , |
Note. A tag that must persist is an explicit value with its own versioning.
use std.inspect.TypeId
fn demo() -> string:
"${TypeId::of::[i32]()}" # "i32"See also: Runtime Boundary, Wasm Boundary.
#Inspectable Types
The compiler supplies Inspectable for exactly the inspectable types:
| Rule | Inspectable type |
|---|---|
| trait.inspectable.primitivePrimitive | the primitive types and string |
| trait.inspectable.declaredDeclared | a struct, enum, or newtype declared at module level, public or private, applied to inspectable type arguments |
| trait.inspectable.collectionsCollection | List[T] and Map[K, V] with inspectable type arguments, and tuples of inspectable elements |
| trait.inspectable.reference-typeReference | a reference whose referent is inspectable or is a trait value type |
| trait.inspectable.parameterType parameter | a type parameter bounded by Inspectable or by a trait that has it as a supertrait |
- trait.inspectable.exactThe compiler supplies
Inspectablefor exactly the types in the table above. - trait.inspectable.optionThe declared case includes
OptionandResult, soT?is inspectable whenTis. - trait.inspectable.fieldsWhat the fields hold does not matter. A struct with a function-typed field, and a newtype over a function type, are inspectable, because identity is the declaration.
- trait.inspectable.argument-examples.reference-values
List[*dyn Display],Result[void, FsError], andMap[string, *dyn Any]are inspectable.
use std.inspect.TypeId
fn demo() -> string:
"${TypeId::of::[List[i32]]()}" # inspectable: a collection of an inspectable argument#Types That Are Not Inspectable
These types are not inspectable, as values or as type arguments:
| Rule | Not inspectable |
|---|---|
| trait.inspectable.not.functionFunction | function types, spelled or as the constructors Fn and SuspendFn from std.function, closures, and function values |
| trait.inspectable.not.suspendSuspension | Suspend[T] and suspension frames |
| trait.inspectable.not.localLocal declaration | struct, enum, newtype, and trait declarations local to a block suite |
| trait.inspectable.not.neverNever | never |
| trait.inspectable.not.argumentArgument | a type applied to a non-inspectable argument, such as Box[fn() -> i32] |
| trait.inspectable.not.parameterUnbounded parameter | a type parameter without an Inspectable bound |
use std.inspect.Inspectable
fn erase() -> void:
let erased: &dyn Inspectable = fn(x: i32) -> i32: x + 1 # error: type-mismatch
pass#Erasure To Inspectable
A concrete reference converts to &dyn Inspectable by an expected type.
- trait.erase.expected.reference-valuesA concrete reference converts to
&dyn Inspectablewhen its referent is inspectable, by Trait Value References. - trait.erase.no-castThere is no cast operator.
- trait.erase.child-implA trait that extends
Inspectablestill needs its own explicit implementation; only itsInspectablepart is supplied. Converting a value without one is an error. Error:type-mismatch. - trait.erase.recorded.reference-valuesThe recorded type is the static type of the referent at the erasure site.
- trait.erase.parameterFor a value of a type parameter
T < Inspectable, the recorded type is the typeTis instantiated with, which the bound supplies at run time. - trait.erase.builtA value built from
T, such as aBox[T], recordsBoxapplied to that type. - trait.erase.bound-required.reference-valuesErasing a reference to a type parameter requires an
Inspectablebound on its referent type. A reference without that evidence is not assignable to&dyn Inspectable. Error:type-mismatch. - trait.erase.widen.reference-valuesWidening a dynamic value of a trait that extends
InspectabletoInspectableis ordinary supertrait widening, bytrait.dyn.ref.widen. - trait.erase.keepsThe widened value keeps its recorded concrete type. Nothing is wrapped twice, including when a type parameter is instantiated with such a trait value type.
use std.inspect.{Inspectable, TypeId}
struct Box[T]:
value: T
fn erase[T < Inspectable](value: T) -> *dyn Inspectable:
&Box { value: value }
fn is_int_box(value: &dyn Inspectable) -> bool:
value.runtime_type() == TypeId::of::[Box[i32]]()
fn read_box(value: &dyn Inspectable) -> i32:
match value.downcast_ref::[Box[i32]]().map(fn(found): *found):
.Some(found) => found.value
.None => 0With let one: i32 = 1, erase(one) records Box[i32], so is_int_box
returns true for it and read_box returns 1. erase(1) records
Box[usize], by the
default type.
erase("one") records Box[string], and both functions take their other
branch.
use std.inspect.Inspectable
fn erase[T](value: T) -> *dyn Inspectable:
value # error: type-mismatchSee also: Assignability And Coercion.
#Recovering A Concrete Type
- trait.downcast.reference
value.downcast_ref::[T]()on&dyn Inspectablereturns.Someof a*Texactly when its recorded concrete type hasT's runtime identity, and.Noneotherwise. - trait.downcast.mutable-reference
value.downcast_mut::[T]()on&mut dyn Inspectableperforms the same test and returnsOption[*mut T]. - trait.downcast.reference-identityThe recovered reference refers to the original place, so
isholds between it and the reference erased to the trait value. - trait.downcast.exact.reference-valuesType arguments must match exactly: an erased
Box[i32]is not aBox[i64], and an erasedList[FsError]is not aList[*dyn Error]. - trait.downcast.no-conversionNo variance, numeric conversion, optional unwrapping, newtype unwrapping, or supertrait search takes place.
- trait.downcast.optional-referenceAn erased reference to
User?is recovered with targetUser?, givingOption[*User?], and never with targetUser. - trait.downcast.copy-through-referenceDereferencing the recovered reference with
*foundcopies its value under the ordinary value-copy rules.
use std.inspect.Inspectable
struct User:
name: string
fn user_name(value: &dyn Inspectable) -> string:
match value.downcast_ref::[User]():
.Some(user) => user.name
.None => "not a user"
fn number(value: &dyn Inspectable) -> i32:
match value.downcast_ref::[i32]():
.Some(found) => *found
.None => -1Why. Recovery preserves the erased reference; copying remains the ordinary
*foundoperation for structs, enums and other values.
#Ordinary Rules For Recovery
- trait.downcast.reference-methods.reference-values
downcast_refanddowncast_mutare reference-receiver methods in the inherent blocks ofdyn Inspectableanddyn Error. - trait.downcast.reference-receivers.reference-valuesThe ordinary reference-receiver rules govern both recovery methods.
- trait.downcast.evidenceThe
Inspectableevidence forT, passed with each call like the evidence for any bound, carries the runtime identity ofT. - trait.downcast.pass-reference-targetA generic function passes its target through an
Inspectablebound, as infn get[T < Inspectable](value: &dyn Inspectable) -> (*T)?. - trait.downcast.shared-result
downcast_refreturns a shared reference, whose immediate referent cannot be written. Error:not-writable. - trait.downcast.mutable-receiver
downcast_muttakes&mut self, so calling it through a shared reference is an error. Error:not-writable. - trait.downcast.targetThe target
Tis written, or inferred from the expected type, at the call. - trait.downcast.visibilityThe target must be nameable there under ordinary visibility, so a private type of another module cannot be recovered outside it.
- trait.downcast.boundA target that fails a bound is an error. Error:
unsatisfied-trait-bound. - trait.downcast.concrete-targetThe target is the referent's type; a reference-valued referent requires its reference type as the target.
use std.inspect.Inspectable
fn get[T < Inspectable](value: &dyn Inspectable) -> (*T)?:
value.downcast_ref()Note. Code first converts a concrete reference to
&dyn Inspectableor&dyn Error, then calls the inherent recovery method.
#Limits Of Runtime Identity
- trait.limit.no-trait-testsNo trait tests. A test compares two runtime identities. Nothing asks whether a value implements a trait, and a dynamic value of one trait is never converted to an unrelated trait.
- trait.limit.no-requirement-keysNo inspectable requirement keys. A trait that is
Inspectableor has it as a supertrait is never a requirement key. A provider value therefore cannot be tested to recover a concrete provider. - trait.limit.signatures.reference-valuesVisible in signatures. Only a value of an inspectable type can be erased to
&dyn Inspectable. A value of an unbounded type parameter, ofAny, or of a trait value type whose trait does not extendInspectablecannot. - trait.limit.signatures.branchA function can therefore branch on a value's type only when a parameter type names
Inspectableor a trait extending it. A parameter bounded by one of them also qualifies. - trait.limit.reference-deterministicDeterministic.
runtime_type, theTypeIdoperations, anddowncast_refare pure functions of the build and the value, and call no provider.
use std.inspect.Inspectable
struct User:
name: string
fn demo(value: &dyn Inspectable) -> string:
match value.downcast_ref::[User]():
.Some(user) => user.name # branches only because the parameter names Inspectable
.None => "not a user"See also: Requirement Rows.
#Embedding And Trait Satisfaction
Embedding never grants trait conformance: the outer type must declare its own implementation.
- trait.embed.no-conformanceEmbedding never grants trait conformance: an outer type implements a trait, and satisfies its bound, only through its own implementation.
An implementation that reuses an embedded type's behavior forwards to it
explicitly, as in fn label(self) -> string: self.Base.label(), or with
by. A bodyless implementation of a trait with a
required method is therefore missing-trait-method, even when an embedded
type has a matching method:
trait Describe:
fn describe(self) -> string
struct Label:
text: string
impl Label:
pub fn describe(self) -> string: self.text
struct Page:
Label
impl Describe for Page # error: missing-trait-methodNote. A forwarding implementation is called as
Trait::method(x), as inDescribe::describe(service), and the embedded type's method through its path, as inservice.Logger.describe(). Where the trait is available, a dot call meets both methods and isambiguous-method, asnames.method-lookup.ambiguousstates.
See also: Trait Delegation, Data Embedding.
#Trait Methods Of Embedded Types
An embedded type's trait methods are not promoted, as
names.promote.member
states. If Label implements Describe and Page embeds Label,
page.describe() never calls Label's implementation, and Page
satisfies no Describe bound unless Page itself implements Describe:
trait Describe:
fn describe(self) -> string
struct Label:
text: string
impl Describe for Label:
fn describe(self) -> string:
self.text
struct Page:
Label
fn invalid(page: Page) -> string:
page.describe() # error: unknown-methodNote. For the error revamp: a
Pagemay have nodescribeof its own, no promoted one, and no available trait method with that name. Then theunknown-methodmessage should suggestpage.Label.describe().
See also: Member Resolution.
#Trait Delegation
A trait implementation may delegate the trait to an embedded field of its
target by naming the field after by:
trait Describe:
fn describe(self) -> string
fn headline(self) -> string:
"* " + self.describe()
struct Logger:
name: string
impl Describe for Logger:
fn describe(self) -> string:
self.name
struct Service:
Logger
port: i32
impl Describe for Service by Logger
struct Worker:
Logger
impl Describe for Worker by Logger:
fn headline(self) -> string:
"worker " + self.Logger.headline()- trait.by.formA trait implementation may delegate the whole trait to an embedded field of its target by naming the field after
by, as inimpl Trait for C by E. - trait.by.valid-partIn
impl Trait for C by E, whereEis notStructure,Cmust be a struct, andEmust name one of its embedded fields. - trait.by.part-implThe type of that field, with
C's type arguments substituted, must implement the same instantiation ofTrait. - trait.by.invalidOtherwise the implementation is an error, reported on the line of
by. Error:invalid-delegation. - trait.by.structure
impl Trait for C by Structureis never a delegation. It declares a derivation template or a derivation block, as Typed Derivation defines. - trait.by.no-traitA header without a trait never delegates:
impl C by Structuredeclares a derivation block with no trait. - trait.by.no-trait.error
impl C by Ewithout a trait, whereEis notStructure, is an error, reported on the line ofby. Error:invalid-delegation.
trait Describe:
fn describe(self) -> string
struct Logger:
name: string
impl Describe for Logger:
fn describe(self) -> string:
self.name
struct Service:
Logger
port: i32
impl Describe for Service by port # error: invalid-delegation
impl Service by Logger # error: invalid-delegation#Generated Methods
- trait.by.generatedFor each method
mofTraitwith a receiver, defaults included, that the body does not write, the implementation has a forwarding method. It hasm's signature, so its inputs followm's inputs tuple, a rest element included. Its body isTrait::m(self.E, arguments...), which passes a vararg on as a spread, and it is checked as if written. - trait.by.writtenA method written in the body replaces the generated one and follows the rules of Implementation Declarations, as does any other member of the body.
impl Describe for Service by Logger above therefore means:
impl Describe for Service:
fn describe(self) -> string:
Describe::describe(self.Logger)
fn headline(self) -> string:
Describe::headline(self.Logger)Note. The forwarding call runs the embedded field's implementation with the field as its receiver. So a default body there calls the field's methods, never the delegating implementation's: there is no overriding. A
&mut selfmethod forwards throughself.E, which is writable through&mut self.
See also: Data Embedding, Member Resolution.
#Delegated Associated Items
- trait.by.assoc-typesAssociated types take the embedded field's bindings: each associated type of the delegating implementation is bound to the type that the field's implementation binds.
- trait.by.assoc-bindingAn associated type binding in the body is an error. Error:
invalid-delegation. - trait.by.assoc-fnAssociated functions have no receiver to forward through and are never generated. The body writes each one, as in any implementation, unless the trait gives it a default. A missing one is an error. Error:
missing-trait-method.
trait Named:
fn name(self) -> string
fn kind() -> string
struct Logger:
label: string
impl Named for Logger:
fn name(self) -> string:
self.label
fn kind() -> string:
"logger"
struct Service:
Logger
impl Named for Service by Logger # error: missing-trait-method#Delegation Is An Ordinary Implementation
- trait.by.ordinaryOtherwise a delegating implementation is an ordinary trait implementation, under every rule for one. Those are Implementation Ownership, Overlap, trait visibility, bounds, dynamic trait values, and Method Resolution.
trait Describe:
fn describe(self) -> string
struct Logger:
name: string
impl Describe for Logger:
fn describe(self) -> string:
self.name
struct Service:
Logger
port: i32
impl Describe for Service by Logger#Default-Method Conflicts
A type that implements two traits with same-named default methods makes a dot call of that name ambiguous where both traits are available.
- trait.conflict.ambiguous-availableA type may implement two traits whose default methods share a name. When both traits are available, a dot call of that name is an error. Each implementation is still valid alone. Error:
ambiguous-method. - trait.conflict.inherentThe type may define an inherent method to provide its ordinary dot-call behavior.
- trait.conflict.qualifiedThe caller may instead use
Trait::method(value, ...)to select one implementation.
trait Named:
fn label(self) -> string:
"named"
trait Tagged:
fn label(self) -> string:
"tagged"
struct User:
name: string
impl Named for User
impl Tagged for User
fn invalid(user: User) -> string:
user.label() # error: ambiguous-method#Unsupported Trait Extensions
- trait.unsupported.listThe language has no specialization, negative implementations, implicit structural conformance, trait-to-trait assertions, or type tests other than exact-type recovery from
Inspectablevalues. - trait.unsupported.abiDynamic trait-value representation is an ABI detail and must preserve the dispatch semantics in this chapter.
@derive(Eq)
struct Badge:
text: string
impl Eq for Badge: # error: overlapping-impl
fn eq(self, other: Badge) -> bool:
self.text == other.textSee also: Runtime Type Identity.