Traits
Defining a Trait
Section titled “Defining a Trait”A trait is a collection of method signatures — and optionally, method bodies — that describe a capability. Any type that promises to provide those methods is said to implement the trait.
trait Greet { fn hello(&self) -> String;}This declares a single required method. Any struct that wants to be Greet-able must provide a concrete hello implementation.
Implementing a Trait
Section titled “Implementing a Trait”Use impl TraitName for TypeName to fulfill a trait contract:
struct Dog { name: String,}
impl Greet for Dog { fn hello(&self) -> String { format!("Woof! I'm {}.", self.name) }}Once Dog implements Greet, you can call .hello() on any Dog value:
let d = Dog { name: String::from("Rex") };println!("{}", d.hello()); // Woof! I'm Rex.Default Methods
Section titled “Default Methods”A trait can provide a default body for any method. Implementors can override it or use the default:
trait Shape { fn area(&self) -> f64;
// Default method — implementors get this for free fn describe(&self) -> String { format!("I am a shape with area {:.2}", self.area()) }}Circle and Rectangle only need to implement area. The describe method is inherited automatically and calls their concrete area at runtime.
Calling Trait Methods
Section titled “Calling Trait Methods”Trait methods are called with the usual dot syntax, as long as the trait is in scope. If multiple traits provide the same method name, you can use fully qualified syntax to disambiguate:
<Circle as Shape>::area(&c)In practice, the dot syntax works in the vast majority of cases.
Traits as Function Parameters
Section titled “Traits as Function Parameters”You can accept any type that implements a trait using impl TraitName in a function parameter:
fn print_area(shape: &impl Shape) { println!("Area: {:.2}", shape.area());}This is shorthand for a generic function with a trait bound, which you’ll learn more about in the Generics lesson.
Try It
Section titled “Try It”The playground below defines a Shape trait with a required area method and a default describe method. Both Circle and Rectangle implement area, and they inherit describe for free.
trait Shape { fn area(&self) -> f64; fn describe(&self) -> String { format!("I am a shape with area {:.2}", self.area()) }}
struct Circle { radius: f64 }struct Rectangle { width: f64, height: f64 }
impl Shape for Circle { fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius }}
impl Shape for Rectangle { fn area(&self) -> f64 { self.width * self.height }}
fn main() { let c = Circle { radius: 3.0 }; let r = Rectangle { width: 4.0, height: 5.0 };
println!("Circle area: {:.2}", c.area()); println!("{}", c.describe()); println!("Rectangle area: {:.2}", r.area()); println!("{}", r.describe());}Compiling…