C.W.K.
Stream
Lesson 04 of 07 · published

Trait Bounds

~12 min · traits, bounds, where

Level 0Rust Curious
0 XP0/80 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete

The previous lesson hit a wall: a bare T can't be compared or printed. Trait bounds are how you tell the compiler 'this T has these capabilities' — and suddenly the generic code works.

Constraining a type parameter

fn largest<T: PartialOrd>(list: &[T]) -> &T says 'T must implement PartialOrd' — the trait behind the > and < operators. Now the body can compare values and the compiler accepts it. The bound is a promise: callers may only use types that satisfy it, and in exchange the body may use everything that trait provides.

Multiple bounds and where clauses

Stack bounds with +: T: Display + Clone means T must be both printable and cloneable. When the bounds pile up, a where clause moves them below the signature for readability — same meaning, cleaner to read when there are several type parameters each with several bounds.

A trait bound is a capability contract. T: Display reads 'whatever T is, it can be printed.' The bound is simultaneously a restriction on callers (only printable types allowed) and a permission for the body (you may print a T). That symmetry is the heart of generic Rust.

impl Trait shorthand

For a single bound on a parameter, fn notify(item: &impl Summary) is shorthand for fn notify<T: Summary>(item: &T). It reads naturally — 'take anything that implements Summary.' Use it for simple cases; reach for the explicit <T> form when you need to name the type or reuse it across parameters.

Code

Bounds unlock the operations the body needs·rust
use std::fmt::Display;

// T must support comparison (>) — the PartialOrd bound unlocks it
fn largest<T: PartialOrd>(list: &[T]) -> &T {
    let mut biggest = &list[0];
    for item in list {
        if item > biggest { biggest = item; }
    }
    biggest
}

// impl Trait shorthand for a single bound
fn announce(item: &impl Display) {
    println!("News! {item}");
}

fn main() {
    println!("{}", largest(&[3, 7, 2, 9, 4])); // works on i32
    println!("{}", largest(&['c', 'a', 'z'])); // and on char
    announce(&"fearless");
}

External links

Exercise

Write fn print_all<T: Display>(items: &[T]) that prints each item. Then add a second bound so it also clones the first item and returns it: <T: Display + Clone>. Rewrite the multi-bound signature using a where clause. Which form reads better as bounds grow?
Hint
Inline bounds (<T: Display + Clone>) are fine for one or two; a where clause keeps the signature line clean once you have multiple parameters each carrying multiple bounds.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue
💛 by Ttoriwarm

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.