← Back to Rust Course | Chapter 11: Generics & Traits | Lesson 7 of 7

Where Clauses

A where clause is a tidy list, off to the side, of all the rules a function's types have to follow.

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

markup
use std::fmt::Display;

fn describe<T>(item: T) -> String
where
    T: Display,
{
    format!("Item: {}", item)
}

fn main() {
    println!("{}", describe(99));
}

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

markup
use std::fmt::{Debug, Display};

fn compare<T, U>(a: T, b: U) -> String
where
    T: Display,
    U: Debug,
{
    format!("{} vs {:?}", a, b)
}

fn main() {
    println!("{}", compare(5, vec![1, 2]));
}

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

markup
fn print_all<T>(items: Vec<T>)
where
    T: std::fmt::Display,
{
    for item in items {
        println!("{}", item);
    }
}

fn main() {
    print_all(vec![1, 2, 3]);
}

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

markup
fn largest<T: PartialOrd + Copy>(items: &[T]) -> T
where
    T: std::fmt::Debug,
{
    let mut largest = items[0];
    for &item in items {
        if item > largest {
            largest = item;
        }
    }
    largest
}

fn main() {
    let numbers = [3, 7, 2, 9, 4];
    println!("{:?}", largest(&numbers));
}
Common Mistakes
  1. Cramming many trait bounds directly into a generic parameter list, making the function signature hard to read, instead of using a where clause.
  2. Assuming where clauses can express something inline bounds cannot -- they are equivalent in power, just more readable for complex cases.
  3. Forgetting a where clause still needs each bound to be satisfiable by the actual types used when the function is called.
Chapter Summary
  • where clauses 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.
  • where clauses can express bounds on complex types, such as Vec<T> needing T: Clone.
  • Idiomatic Rust prefers where clauses 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:

Login to run this code

C/C++/Java/PHP execution requires a free account. Your code is saved — you'll land right back in the editor after logging in.