Trait bounds
A plain T can be any type, so Rust lets you do almost nothing with it. A bound says which traits T must implement. Then you can use those traits' methods.
T: Trait
Write the bound after the type parameter.
use std::fmt::Display;fn show<T: Display>(x: T) { println!("[{x}]");}Display is the trait that {} uses to print a value. It lives in the standard library at std::fmt::Display. use std::fmt::Display; at the top lets you write just Display. Without the use, write the full path, like T: std::fmt::Display.
Now show accepts any type that implements Display. Join several bounds with +, like T: Display + Clone. Calling show with a type that is not Display fails with E0277.
Missing bounds
If the bound is missing, Rust will not guess.
You use on a T | Error without a bound |
|---|---|
a + b or a > b | E0369 |
a method like x.area() | E0599 |
{} in println! | E0277 |
Fix it by adding the bound, such as T: PartialOrd for >.
where clauses
Long bounds can move after the signature. These two mean the same thing:
fn f<T: Display + Clone>(x: T) {}fn f<T>(x: T)where T: Display + Clone,{}impl Trait arguments
impl Trait in an argument is a short way to write a bound.
fn show(x: impl Display) { println!("[{x}]");}impl Trait returns
-> impl Trait returns some type that implements the trait. The caller only knows the trait, not the real type.
fn label() -> impl Display { 42}The function must return one single type. Returning an i32 in one branch and a &str in another fails with E0308.
Remember
- Bounds unlock methods and operators on
T. whereis only a different place to write bounds.impl Traitis short for a bound in arguments and hides the real type in returns.