capypad
0 day streak
rust / intermediate
Snippet

Generic Functions with where Clauses: Advanced Trait Bounds

Generic functions in Rust become powerful with trait bounds that constrain what types are acceptable. The where clause syntax provides a cleaner alternative to inline trait bounds, especially with multiple constraints. Here T: Display + Debug + Clone means T must implement all three traits. You can also have multiple type parameters (T, U) with different constraints for each, enabling flexible function signatures.

snippet.rs
rust
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
use std::fmt::{Display, Debug};
 
#[derive(Debug)]
struct Container<T> {
value: T,
}
 
impl<T> Container<T> {
fn new(value: T) -> Self {
Container { value }
}
}
 
trait Printable {
fn format_for_print(&self) -> String;
}
 
impl<T: Display + Debug> Printable for Container<T> {
fn format_for_print(&self) -> String {
format!("Debug: {:?}, Display: {}", self.value, self.value)
}
}
 
fn print_all<T>(items: &[T], prefix: &str)
where
T: Display + Debug + Clone,
{
for item in items {
println!("{}: {:?}", prefix, item);
}
}
 
fn compare_and_print<T, U>(left: &T, right: &U)
where
T: Display + PartialOrd,
U: Display + Debug,
{
println!("Left: {}, Right: {:?}", left, right);
}
 
fn main() {
let nums = Container::new(42);
println!("{}", nums.format_for_print());
print_all(&[1, 2, 3], "Number");
compare_and_print(&10.5, &"hello");
}
Breakdown
1
where T: Display + Debug + Clone,
where clause groups multiple trait bounds for readability
2
for item in items {
Iterates over borrowed slice without taking ownership
3
fn compare_and_print<T, U>(left: &T, right: &U)
Multiple type parameters each with their own trait constraints
4
T: Display + PartialOrd, U: Display + Debug
Different bounds for different generic parameters