A trait declares a set of associated items that a type can implement. Traits are
Rust’s way of sharing behaviour: they serve as interfaces, bounds on
generic parameters and, through
dyn, as dynamically
dispatched object types (Reference: Traits, Implementations, Associated items).
Declaring a trait
Trait:
unsafe? trait IDENTIFIER GenericParams? ( : Bounds? )? WhereClause?
{ InnerAttribute* AssociatedItem* }
AssociatedItem:
OuterAttribute* ( MacroInvocationSemi | Visibility? ( TypeAlias | ConstantItem | Function ) )
trait Shape {
const SIDES: u32; // associated constant
type Unit; // associated type
fn area(&self) -> f64; // required method
fn describe(&self) -> String { // provided method, with a default body
format!("area {}", self.area())
}
fn unit_square() -> Self where Self: Sized; // associated function, no receiver
}
A trait item may have a visibility in the grammar, but it is rejected when the trait
is checked: every item has the trait’s visibility. Parameter names are required since
edition 2018; edition 2015 also allows anonymous parameters. A function in a trait
without a body may use only identifiers or _ as its parameter patterns.
One with a body may use any irrefutable pattern.
Implementations
Implementation: InherentImpl | TraitImpl
InherentImpl:
impl GenericParams? Type WhereClause? { InnerAttribute* AssociatedItem* }
TraitImpl:
unsafe? impl GenericParams? !? TypePath for Type WhereClause?
{ InnerAttribute* AssociatedItem* }
struct Circle { r: f64 }
impl Circle { // inherent implementation
fn new(r: f64) -> Self { Circle { r } } // associated function
fn radius(&self) -> f64 { self.r } // method
}
impl Shape for Circle { // trait implementation
const SIDES: u32 = 0;
type Unit = f64;
fn area(&self) -> f64 { 3.14 * self.r * self.r }
fn unit_square() -> Self { Circle::new(1.0) }
}
let c = Circle::new(2.0);
c.area(); // method call
Shape::area(&c); // the same, by trait path
<Circle as Shape>::area(&c); // the same, fully qualified
- An inherent implementation is written in the crate that defines the type and can add associated functions and constants, but not associated types.
- A trait implementation must define every required associated item and may override the provided ones. Its signatures must match the trait’s.
- A trait must be in scope, usually by
useor the prelude, for its methods to be called withx.foo(). Selfnames the implementing type inside the implementation and the implementing type’s placeholder in a trait.- A negative implementation,
impl !AutoTrait for T, can be written only by the standard library.
Generic parameters on impl must constrain the implementation: each type
or const parameter must appear in the implemented trait, the implementing type or an
associated type in a bound of a type that does (Reference: Implementations). A
lifetime parameter must do so only if it appears in an associated type.
Methods and receivers
A function whose first parameter is named self is a method and
can be called with x.foo(). The shorthand forms expand to typed forms:
| Shorthand | Equivalent |
|---|---|
self | self: Self |
&self | self: &Self |
&mut self | self: &mut Self |
&'a self | self: &'a Self |
A receiver may also be written with one of these explicit types, where
S is Self or another such type: &S,
&mut S, Box<S>, Rc<S>,
Arc<S>, or Pin<P> where P is one of those.
A mut prefix makes the receiver a mutable binding. A method call finds its
receiver by trying the value, then references and dereferences to it, in that order.
Associated types
An associated type is a type alias tied to a trait. It is written with no default in
the trait, because a default is not allowed, and must be defined in each
implementation. For a type parameter T, T::Item refers to it;
in general, the form is <T as Trait>::Item.
trait Container {
type Item;
fn get(&self, index: usize) -> Option<&Self::Item>;
}
impl<T> Container for Vec<T> {
type Item = T;
fn get(&self, index: usize) -> Option<&T> { self.as_slice().get(index) }
}
Generic associated types
An associated type may take its own generic parameters, called a generic associated type (GAT). Where clauses that can be proven on the functions using the GAT must also be written on the GAT itself.
trait LendingIterator {
type Item<'x> where Self: 'x;
fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}
Associated constants
A trait may declare const NAME: Type;, with or without a default value;
implementations may override it. Inherent implementations can also have associated
constants. An associated constant is evaluated only when it is used, and one that
depends on generic parameters is evaluated after substitution.
trait Named { const NAME: &'static str = "unknown"; }
struct A;
impl Named for A {} // uses the default
struct B;
impl Named for B { const NAME: &'static str = "b"; }
Supertraits
A supertrait is a trait that must be implemented for a type to implement another
trait. Declaring trait Circle: Shape is the same as
trait Circle where Self: Shape. Wherever Circle is used as a
bound, or a dyn Circle value is held, the supertrait’s items are
available. A trait cannot be its own supertrait.
trait Named { fn name(&self) -> String; }
trait Employee: Named { // implementers must also be Named
fn id(&self) -> u32;
fn summary(&self) -> String {
format!("{} ({})", self.name(), self.id())
}
}
let d: &dyn Employee = make_employee();
let n: &dyn Named = d; // upcast to a supertrait object
Coherence and the orphan rule
A trait implementation is incoherent, and rejected, if it fails the orphan rule or overlaps another implementation. Two implementations overlap if they can be instantiated with the same type (Reference: Implementations).
The orphan rule says a trait implementation is allowed only if the trait or at least
one of the types in it is defined in the current crate. Formally, given
impl<P1..=Pn> Trait<T1..=Tn> for T0, the implementation is valid only
if one of these holds:
Traitis a local trait.- At least one of
T0..=Tnis a local type, and no uncovered type parameter fromP1..=Pnappears inT0..Ti, whereTiis the first local type.
A type parameter is covered when it appears only inside another type, such as
Vec<T>. Fundamental types are the exception: Box<LocalType>
counts as local, and the T in Box<T> is not considered
covered.
struct MyType;
impl std::fmt::Debug for MyType { /* … */ } // ok: MyType is local
impl From<MyType> for String { /* … */ } // ok: MyType appears as a parameter
// impl std::fmt::Debug for Vec<i32> { } // error: neither is local
// impl<T> From<T> for MyType { } // error: overlaps the reflexive impl of From
Auto traits
Auto traits are implemented by the compiler for most types automatically. They are
Send, Sync, Unpin, UnwindSafe and
RefUnwindSafe (Reference: Special types and traits). Unless a type has an
explicit or negative implementation, the compiler applies these rules:
&T,&mut T,*const T,*mut T,[T; n]and[T]implement the trait ifTdoes.- Function item types and function pointers implement it.
- Structs, enums, unions and tuples implement it if all their fields do.
- Closures implement it if all their captures do.
- A generic implementation for a type blocks the automatic one, so
&TisSendonly ifTisSync. - Negative implementations remove the automatic one:
*mut Tis neverSend.
Auto traits can be extra bounds on a trait object:
Box<dyn Debug + Send + UnwindSafe>. See
Send and
Sync.
Unsafe traits
An unsafe trait has invariants the compiler cannot check, so implementing
it requires unsafe impl. Using a correctly implemented unsafe trait is
safe. Send and Sync are unsafe traits.
Dyn compatibility
Only a dyn compatible trait can be the base trait of a trait object. The Reference used to call this object safety. A trait is dyn compatible if all of these hold (Reference: Traits):
- All supertraits are dyn compatible.
Sizedis not a supertrait: the trait does not requireSelf: Sized.- It has no associated constants.
- It has no associated types with generics.
- Every associated function is either dispatchable or explicitly non-dispatchable.
| Dispatchable functions must | Non-dispatchable functions |
|---|---|
|
Have a where Self: Sized bound. A method taking self by value implies it |
The AsyncFn, AsyncFnMut and AsyncFnOnce traits
are not dyn compatible.
trait Good {
fn work(&self);
fn create() -> Self where Self: Sized; // excluded from dyn with where Self: Sized
}
trait Bad {
fn make() -> Self; // not dispatchable: no receiver, returns Self
fn compare<T>(&self, other: T); // not dispatchable: type parameter
}
fn use_it(x: &dyn Good) { x.work(); }
// fn nope(x: &dyn Bad) {} // error: Bad is not dyn compatible
Operator traits
Operators and special calls are overloaded by implementing traits from
std::ops and std::cmp, listed on the
Expressions page.
Deref and DerefMut also drive method resolution and deref
coercions. Drop provides a destructor. Closures implement
Fn, FnMut and FnOnce.