Trait bounds
| Bound with trait | |
| Where clauses | |
| Combine bounds | |
| Static dispatch |
Bound with trait
A bare placeholder T allows no method calls, since the compiler knows nothing about it. A bound promises that T implements one trait, which unlocks that trait methods. Display as a bound means every item prints with empty braces.
fn print_all<T: std::fmt::Display>(items: &[T]) {
for item in items {
println!("{}", item);
}
}
Integers and strings both implement Display, so both slide through this function. Types without Display are rejected before the program ever runs. The bound is checked at compile time, not at run time.
Where clauses
Long bound lists crowd the function header and hurt readability. A where clause moves those promises below the parameter list with the same meaning. Short headers stay short while complex requirements stay visible.
fn show<T>(value: T)
where
T: std::fmt::Display,
{
println!("{}", value);
}
Value prints because the clause guarantees Display. Adding a second bound later means adding one more line, not rewriting the header. Prefer where clauses once bounds grow past a glance.
Combine bounds
One placeholder can promise several traits at once with plus signs between them. Clone plus Display means values duplicate and print. The function below prints a clone beside the original value.
fn dump<T: std::fmt::Display + Clone>(value: T) {
println!("{} {}", value.clone(), value);
}
The plus sign reads as and, so T must satisfy every listed trait. Missing any one bound is a compile error at the call site. Combined bounds keep flexible functions honest.
Static dispatch
Generic functions compile to a separate copy per concrete type, a strategy called static dispatch. Each copy calls its method directly with no lookup at run time. The total below sums any shape, and every shape gets its own fast path.
trait Shape {
fn area(&self) -> f64;
}
fn total<S: Shape>(shapes: &[S]) -> f64 {
shapes.iter().map(|s| s.area()).sum()
}
Monomorphization writes one total per shape behind the scenes. Direct calls inline well and branch prediction stays trivial. Dynamic dispatch with trait objects trades that speed for flexibility, and a later lesson covers it.
Next: Closures
Article author: Arthur Isaev