Traits
A trait defines a set of methods a type can implement — similar to an interface in other languages. Once a type implements a trait, you can call that trait's methods on it directly.
trait Greet {
fn greet(&self) -> String;
}
struct Person {
name: String,
}
impl Greet for Person {
fn greet(&self) -> String {
format!("Hello, my name is {}", self.name)
}
}
fn main() {
let alice = Person { name: String::from("Alice") };
println!("{}", alice.greet());
}Hello, my name is Alice
Trait bounds
A generic function can require that its type parameter implements a particular trait, written T: Greet. This lets greet_everyone accept a slice of any type that implements Greet, not just Person specifically — the function is written once and works for every type that satisfies the bound.
trait Greet {
fn greet(&self) -> String;
}
struct Person {
name: String,
}
impl Greet for Person {
fn greet(&self) -> String {
format!("Hello, my name is {}", self.name)
}
}
fn greet_everyone<T: Greet>(items: &[T]) {
for item in items {
println!("{}", item.greet());
}
}
fn main() {
let people = vec![
Person { name: String::from("Alice") },
Person { name: String::from("Bob") },
];
greet_everyone(&people);
}Hello, my name is Alice Hello, my name is Bob
Next: generics — writing a function or struct that works across many types, the general mechanism trait bounds build on.
Practice exercise
Define a trait Area with a method area(&self) -> f64. Implement it for a struct Square { side: f64 } so that area() returns side * side. Create a Square with side 4.0 and print its area().