Where Clauses
where clause is a tidy list, off to the side, of all the rules a function's types have to follow.In this page:
fn name<T, U>(a: T, b: U) -> ReturnType
where
T: Trait1 + Trait2,
U: Trait3,
{
// body
}
Basic where Clause
A where clause lists trait bounds separately after the function signature, keeping the parameter list itself clean and easy to scan.
Example: Basic where Clause
// Bring `std::fmt::Display` into scope
use std::fmt::Display;
fn describe<T>(item: T) -> String
where
T: Display,
{
format!("Item: {}", item)
}
// Entry point: execution of the program starts here
fn main() {
// Print "{}" to the console, substituting in `describe(99)` (with a trailing newline)
println!("{}", describe(99));
}
Login to try C/C++/Java/PHP code in the editor
Multiple Bounds in a where Clause
When a function has several generic parameters each needing their own bounds, a where clause lists them all clearly, one per line, instead of a cramped inline list.
Example: Multiple Bounds in a where Clause
// Bring `std::fmt::{Debug, Display}` into scope
use std::fmt::{Debug, Display};
fn compare<T, U>(a: T, b: U) -> String
where
T: Display,
U: Debug,
{
format!("{} vs {:?}", a, b)
}
// Entry point: execution of the program starts here
fn main() {
// Print "{}" to the console, substituting in `compare(5, vec![1, 2])` (with a trailing newline)
println!("{}", compare(5, vec![1, 2]));
}
Login to try C/C++/Java/PHP code in the editor
Bounding a Generic Container Type
where clauses can express bounds on more complex generic types, such as requiring the elements inside a Vec<T> to implement a particular trait.
Example: Bounding a Generic Container Type
fn print_all<T>(items: Vec<T>)
where
T: std::fmt::Display,
{
// Loop over `items`, binding each item to `item`
for item in items {
// Print "{}" to the console, substituting in `item` (with a trailing newline)
println!("{}", item);
}
}
// Entry point: execution of the program starts here
fn main() {
print_all(vec![1, 2, 3]);
}
Login to try C/C++/Java/PHP code in the editor
where Clauses vs Inline Bounds
Inline bounds and where clauses express exactly the same constraints -- the choice is purely about readability, with where clauses generally preferred once bounds become numerous or complex.
Example: where Clauses vs Inline Bounds
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T
where
T: std::fmt::Debug,
{
// Declare a mutable variable `largest`, initialized to `items[0]`
let mut largest = items[0];
// Loop over `items`, binding each item to `&item`
for &item in items {
// Check whether `item > largest`
if item > largest {
largest = item;
}
}
largest
}
// Entry point: execution of the program starts here
fn main() {
// Declare an immutable variable `numbers`, set to `[3, 7, 2, 9, 4]`
let numbers = [3, 7, 2, 9, 4];
// Print "{:?}" to the console, substituting in `largest(&numbers)` (with a trailing newline)
println!("{:?}", largest(&numbers));
}
Login to try C/C++/Java/PHP code in the editor
- Cramming many trait bounds directly into a generic parameter list, making the function signature hard to read, instead of using a
whereclause. - Assuming
whereclauses can express something inline bounds cannot -- they are equivalent in power, just more readable for complex cases. - Forgetting a
whereclause still needs each bound to be satisfiable by the actual types used when the function is called.
whereclauses move trait bounds out of the angle brackets into a dedicated section after the function signature.- They are functionally equivalent to inline bounds, but greatly improve readability for functions with multiple constrained parameters.
whereclauses can express bounds on complex types, such asVec<T>needingT: Clone.- Idiomatic Rust prefers
whereclauses once a function has more than one or two simple bounds.
Chapter Quiz — Complete all 7 topics to unlock
0/7 topics done
Complete these topics first: