Rust Traits
A trait is a list of behaviours that types can agree to provide. Very different types, like a dog and a robot, can provide the same behaviour, like speaking.
Declare the behaviour
A trait lists the method names and types, with no bodies.
trait Describe {
fn describe(&self) -> String;
}Provide it for one type
An impl block gives one type the promised behaviour.
impl Describe for Dog {
fn describe(&self) -> String {
format!("{} the dog", self.name)
}
}Call it like any method
A value with the trait answers the trait's method like any other method.
let d = Dog { name: String::from("Rex") };
println!("{}", d.describe());A default method can call a required one
Write the shared method once, as a default in the trait. Each type supplies only the small piece it names.
trait Greeter {
fn name(&self) -> &str;
fn greet(&self) -> String {
format!("Hello, {}!", self.name())
}
}Derive the common traits
For everyday traits, one derive line asks Rust to write the impls for you: Debug, Clone, PartialEq, Hash, and more.
#[derive(Debug, Clone, PartialEq)]
struct Dog {
name: String,
}Your own methods can call trait methods
Code inside impl Dog may use the Describe methods that the same Dog provides.
impl Dog {
fn announce(&self) -> String {
format!("Here comes: {}", self.describe())
}
}Example
trait Describe { fn describe(&self) -> String; }
struct Dog { name: String }
impl Describe for Dog {
fn describe(&self) -> String { format!("{} the dog", self.name) }
}
fn main() { let d = Dog { name: String::from("Rex") }; println!("{}", d.describe()); }Key points
- The orphan rule: you may implement a trait only if the trait or the type belongs to your crate
- A default method cannot call the overriding version of itself — an override replaces it completely
- A trait may declare an associated function.
- It returns Self, like fn new(name: &str) -> Self.
- Calling it needs a type annotation.
- You call it as Animal::new("Dolly").
- You write let dolly: Sheep = ... for the annotation.
- Self is unknown until you name it.
- Without one, Rust fails.
- It fails with error E0282
- When you write a + b or print a value with {:?}, a trait is at work behind the scenes.
- Interfaces in other languages do a similar job.
- A trait sets out behaviours that each type then provides for itself.
In this example, the trait Describe promises one method, describe, which returns a String. Dog is a simple struct with a name. The impl block gives Dog the promised behaviour: it builds the text "Rex the dog" from the name. In main, calling d.describe() runs that code and prints Rex the dog. Any other type could implement Describe too, and code written against the trait would work with all of them.
Note
Traits are also how Rust handles operators, printing, and iteration.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What is the orphan rule?