Traits
Syntax
Trait :
unsafe
?trait
IDENTIFIER Generics? (:
TypeParamBounds? )? WhereClause?{
TraitItem\*
}
TraitItem :
OuterAttribute\* (
TraitFunc
| TraitMethod
| TraitConst
| TraitType
| MacroInvocationSemi
)TraitFunc :
TraitFunctionDecl (;
| BlockExpression )TraitMethod :
TraitMethodDecl (;
| BlockExpression )TraitFunctionDecl :
FunctionQualifiersfn
IDENTIFIER Generics?
(
TraitFunctionParameters?)
FunctionReturnType? WhereClause?TraitMethodDecl :
FunctionQualifiersfn
IDENTIFIER Generics?
(
SelfParam (,
TraitFunctionParam)\*,
?)
FunctionReturnType? WhereClause?TraitFunctionParameters :
TraitFunctionParam (,
TraitFunctionParam)\*,
?TraitFunctionParam† :
( Pattern:
)? TypeTraitConst :
const
IDENTIFIER:
Type (=
Expression )?;
TraitType :
type
IDENTIFIER (:
TypeParamBounds? )?;
A trait describes an abstract interface that types can implement. This interface consists of associated items, which come in three varieties:
All traits define an implicit type parameter Self
that refers to "the type
that is implementing this interface". Traits may also contain additional type
parameters. These type parameters, including Self
, may be constrained by
other traits and so forth as usual.
Traits are implemented for specific types through separate implementations.
Items associated with a trait do not need to be defined in the trait, but they may be. If the trait provides a definition, then this definition acts as a default for any implementation which does not override it. If it does not, then any implementation must provide a definition.
Trait bounds
Generic items may use traits as bounds on their type parameters.
Generic Traits
Type parameters can be specified for a trait to make it generic. These appear after the trait name, using the same syntax used in generic functions.
# #![allow(unused_variables)] #fn main() { trait Seq<T> { fn len(&self) -> u32; fn elt_at(&self, n: u32) -> T; fn iter<F>(&self, f: F) where F: Fn(T); } #}
Object Safety
Object safe traits can be the base trait of a trait object. A trait is object safe if it has the following qualities (defined in RFC 255):
- It must not require
Self: Sized
- All associated functions must either have a
where Self: Sized
bound, or- Not have any type parameters (although lifetime parameters are allowed), and
- Be a method that does not use
Self
except in the type of the receiver.
- It must not have any associated constants.
- All supertraits must also be object safe.
Supertraits
Supertraits are traits that are required to be implemented for a type to implement a specific trait. Furthermore, anywhere a generic or trait object is bounded by a trait, it has access to the associated items of its supertraits.
Supertraits are declared by trait bounds on the Self
type of a trait and
transitively the supertraits of the traits declared in those trait bounds. It is
an error for a trait to be its own supertrait.
The trait with a supertrait is called a subtrait of its supertrait.
The following is an example of declaring Shape
to be a supertrait of Circle
.
# #![allow(unused_variables)] #fn main() { trait Shape { fn area(&self) -> f64; } trait Circle : Shape { fn radius(&self) -> f64; } #}
And the following is the same example, except using where clauses.
# #![allow(unused_variables)] #fn main() { trait Shape { fn area(&self) -> f64; } trait Circle where Self: Shape { fn radius(&self) -> f64; } #}
This next example gives radius
a default implementation using the area
function from Shape
.
# #![allow(unused_variables)] #fn main() { # trait Shape { fn area(&self) -> f64; } trait Circle where Self: Shape { fn radius(&self) -> f64 { // A = pi * r^2 // so algebraically, // r = sqrt(A / pi) (self.area() /std::f64::consts::PI).sqrt() } } #}
This next example calls a supertrait method on a generic parameter.
# #![allow(unused_variables)] #fn main() { # trait Shape { fn area(&self) -> f64; } # trait Circle : Shape { fn radius(&self) -> f64; } fn print_area_and_radius<C: Circle>(c: C) { // Here we call the area method from the supertrait `Shape` of `Circle`. println!("Area: {}", c.area()); println!("Radius: {}", c.radius()); } #}
Similarly, here is an example of calling supertrait methods on trait objects.
# #![allow(unused_variables)] #fn main() { # trait Shape { fn area(&self) -> f64; } # trait Circle : Shape { fn radius(&self) -> f64; } # struct UnitCircle; # impl Shape for UnitCircle { fn area(&self) -> f64 { std::f64::consts::PI } } # impl Circle for UnitCircle { fn radius(&self) -> f64 { 1.0 } } # let circle = UnitCircle; let circle = Box::new(circle) as Box<dyn Circle>; let nonsense = circle.radius() * circle.area(); #}
Unsafe traits
Traits items that begin with the unsafe
keyword indicate that implementing the
trait may be unsafe. It is safe to use a correctly implemented unsafe trait.
The trait implementation must also begin with the unsafe
keyword.
Sync
and Send
are examples of unsafe traits.
Parameter patterns
Function or method declarations without a body only allow IDENTIFIER or
_
wild card patterns. mut
IDENTIFIER is currently
allowed, but it is deprecated and will become a hard error in the future.
In the 2015 edition, the pattern for a trait function or method parameter is optional:
# #![allow(unused_variables)] #fn main() { trait T { fn f(i32); // Parameter identifiers are not required. } #}
The kinds of patterns for parameters is limited to one of the following:
- IDENTIFIER
mut
IDENTIFIER_
&
IDENTIFIER&&
IDENTIFIER
Beginning in the 2018 edition, function or method parameter patterns are no longer optional. Also, all irrefutable patterns are allowed as long as there is a body. Without a body, the limitations listed above are still in effect.
# #![allow(unused_variables)] #fn main() { trait T { fn f1((a, b): (i32, i32)) {} fn f2(_: (i32, i32)); // Cannot use tuple pattern without a body. } #}