Rust Where Clauses
A where clause moves trait bounds to their own lines, just before the body. Bounds otherwise sit next to the type letter, like T: Debug.
The same bounds, on their own lines
Nothing changes but the layout — the bounds move after the parameter list, one per line:
fn print_pair<T, U>(left: &T, right: &U)
where T: Debug, U: Debug { }A where clause on an impl block
The bounds are stated once, for every method in the block. Only where can aim a bound at a built type like Option<T>:
impl<T> Wrap for T
where
Option<T>: Debug
{ }Bundle repeated bounds into one trait
Summarize now means Clone + Display everywhere it appears, and every type that has both gets it for free.
trait Summarize: Clone + Display {}
impl<T: Clone + Display> Summarize for T {}Bound the associated type, not just the letter
Here the promise is about I::Item — whatever the iterator produces must print with {}. Only a where clause can aim a bound at a built type like this:
fn print_all<I>(items: I)
where
I: Iterator,
I::Item: Display,
{
for item in items {
println!("item: {item}");
}
}
print_all(vec!["north", "south"].into_iter());Example
use std::fmt::Debug;
fn print_pair<T, U>(left: &T, right: &U)
where
T: Debug,
U: Debug,
{
println!("{:?} and {:?}", left, right);
}
trait Wrap {
fn wrapped(self);
}
impl<T> Wrap for T
where
Option<T>: Debug,
{
fn wrapped(self) {
println!("{:?}", Some(self));
}
}
fn main() {
print_pair(&"left", &42);
vec![1, 2, 3].wrapped();
}Key points
- A where clause means exactly the same as the same bounds written inline.
- It is the same promise, moved.
- It is not a new kind of promise
- Use where on functions, on impl blocks, and on structs — anywhere inline bounds can go
- Bounds written on a struct definition must be restated wherever the struct is named.
- So the usual style keeps struct definitions bare.
- It puts the bounds on impl blocks and functions instead
- The one case that needs where is a bound on a built type like Option<T>.
- A signature with several letters and several bounds gets crowded on its first line.
In this example, print_pair moves its two Debug bounds into a where clause. It puts one bound per line, so the first line stays short. Below, the Wrap impl shows the special power of where. Its bound sits on Option<T>. That is the built type the method actually prints, not T alone. In main, print_pair prints "left" and 42, with quotes because Debug shows text that way. Then the vector wraps itself in Some and prints Some([1, 2, 3]).
Note
Choosing inline bounds or a where clause is mostly style.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
Which bound can a where clause state directly that inline bounds cannot?