🎓 A1Learner
🔍
0 of 149 lessons completed

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?