Trait Bounds
Using trait bounds to constrain generic types.
Advanced⏱️ 45 min📚 Prerequisites: 1
Trait Bounds
Trait bounds allow us to constrain generic types so that only types implementing certain traits are allowed.
Basic Trait Bound
RUSTfn largest<T: PartialOrd>(list: &[T]) -> Option<&T> { // ... }
Multiple Trait Bounds
RUSTfn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 { // ... }
where Clause
RUSTfn some_function<T, U>(t: &T, u: &U) -> i32 where T: Display + Clone, U: Clone + Debug, { // ... }
Trait Bounds in Implementations
RUSTimpl<T: Display> Pair<T> { fn cmp_display(&self) { if self.x >= self.y { println!("The largest x = {}", self.x); } } }
Conditionally Implement Methods
RUSTimpl<T: Display + PartialOrd> Pair<T> { fn cmp_display(&self) { // ... } }
Blanket Implementations
RUSTimpl<T: Display> ToString for T { fn to_string(&self) -> String { // ... } }
Code Examples
Basic Trait Bound
Using trait bounds
RUST
use std::fmt::Display;
fn print_largest<T: PartialOrd + Display>(list: &[T]) -> Option<&T> {
if list.is_empty() {
return None;
}
let mut largest = &list[0];
for item in list {
if item > largest {
largest = item;
}
}
Some(largest)
}
fn main() {
let numbers = vec![34, 50, 25, 100, 65];
if let Some(largest) = print_largest(&numbers) {
println!("The largest number: {}", largest);
}
}Explanation:
The T: PartialOrd + Display trait bound ensures that type T is comparable and printable. This allows the use of the > operator and println!.
where Clause
Using where for complex trait bounds
RUST
use std::fmt::Display;
use std::fmt::Debug;
fn compare_and_print<T, U>(t: &T, u: &U)
where
T: Display + PartialOrd,
U: Display + Debug,
{
println!("t: {}, u: {:?}", t, u);
}
fn main() {
compare_and_print(&5, &"hello");
}Explanation:
The where clause makes complex trait bounds more readable. Especially useful when there are many generic parameters.
Conditional Implementation
Implementing methods based on trait bounds
RUST
use std::fmt::Display;
struct Pair<T> {
x: T,
y: T,
}
impl<T> Pair<T> {
fn new(x: T, y: T) -> Pair<T> {
Pair { x, y }
}
}
impl<T: Display + PartialOrd> Pair<T> {
fn cmp_display(&self) {
if self.x >= self.y {
println!("The largest x = {}", self.x);
} else {
println!("The largest y = {}", self.y);
}
}
}
fn main() {
let pair = Pair::new(5, 3);
pair.cmp_display();
}Explanation:
With trait bounds, we can conditionally implement methods. cmp_display() is only available if T implements Display and PartialOrd traits.
Exercises
Trait Bounds
Write a function with trait bounds!
Starter Code:
RUST
use std::fmt::Display;
fn main() {
print_if_greater(&10, &5);
}