Traits are a mechanism to define shared behavior between types. It is similar to abstract classes in other languages.
Basic definition
trait Greet { fn greet(&self); // Method that must be implemented}
Implementation
impl Greet for Person { fn greet(&self) { println!("Hello, I'm {}", self.name); }}person.greet();
orphan rule
A trait can be implemented as long as:
The type is yours.
The trait is yours.
Both the type and the trait are yours.
This allows adding behavior to types from external libraries:
// custom trait for an external typeimpl Repeat for String { fn repeat(&self) { // implementation... }}s.repeat();// external trait for a custom typestruct Person;impl std::fmt::Display for Person { // implementation...}// external trait for an external typeimpl std::fmt::Display for String { // ERROR}
Default values
A trait can provide default implementations that types can use or override:
trait Greet { fn greet(&self) { println!("Hello, I'm {}", self.name); }}impl Greet for Person {} // Uses the default implementation
Warning
The default implementation has access to self.name, but the compiler cannot verify that all types implementing the trait have a name field. If a type does not have it, you will get an error when implementing.
Trait as parameter
Allows creating functions that accept any type that implements a trait:
Allows a function to return any type that implements a trait:
fn create(name: String, age: u32) -> impl Greet { Person { name, age }}
Warning
When impl Trait is used as a return type, the function can only return a single concrete type. You cannot return Person in one case and Animal in another.
fn create(b: bool) -> impl Greet { // ERROR if b { Person } else { Dog }}
To return any type that implements a trait you must use Box<dyn>. See Smart Pointers.
fn create(b: bool) -> Box<dyn Greet> { if b { Box::new(Person) } else { Box::new(Dog) }}
Derived traits
They can be implemented automatically with #[derive(...)]. See procedural macros:
A trait can require that types implementing it also implement another trait:
trait SayName: Greet { fn name(&self);}// To implement SayName, the type MUST ALSO implement Greet
Tip
Super traits are useful when your trait depends on the behavior of another. For example, Display: Debug guarantees that every type that is displayable is also debuggable.
Traits and standard types
Many standard library types implement common traits:
// Option and Result implement many traitslet opt: Option<i32> = Some(42);println!("{:?}", opt); // Debuglet opt2 = opt.clone(); // Clone
Note
Closures implement the Fn, FnMut and FnOnce traits depending on how they capture variables. This allows passing closures as arguments of generic functions.