Rust Conditional & Blanket Impls
A conditional impl is an impl block with bounds. Its methods exist only for types that meet the bounds.
The pain: one impl per type
Without a blanket impl, every type needs its own copy of the same body:
trait Shout { fn shout(&self) -> String; }
impl Shout for i32 { fn shout(&self) -> String { format!("{}!", self) } }
impl Shout for String { fn shout(&self) -> String { format!("{}!", self) } }
fn main() {
println!("{}", 42.shout());
println!("{}", String::from("hi").shout());
}A door rule on an impl
The bounds on the impl are the rule — the methods inside exist only for a T that meets them:
impl<T: Display + PartialOrd> Pair<T> {
fn show_larger(&self) { /* compares and prints */ }
}Inside, the bounds are promises kept
Because the rule demanded Display + PartialOrd, the method may compare with >= and print with {} freely:
if self.x >= self.y {
println!("larger is x = {}", self.x);
}Outside the rule, the method is missing
A Vec has no Display, so Pair<Vec<i32>> never gets show_larger — calling it is refused:
let pair = Pair { x: vec![1], y: vec![2] };
pair.show_larger(); // error E0599: no method named `show_larger`The library’s own pattern: ToString
std blanket-implements ToString for every Display type. Implement Display by hand — never ToString — and to_string() arrives free.
let text: String = Metre { value: 3.0 }.to_string();
println!("{}", text);The clash: blanket meets specific
Once a blanket impl covers a type, a hand-written impl for that same type is refused. The error is E0119: conflicting implementations. (This was checked with the compiler.)
impl<T: Display> Loud for T { /* ... */ }
impl Loud for i32 { /* refused — i32 is already covered */ }Example
use std::fmt::Display;
struct Pair<T> { x: T, y: T }
impl<T: Display + PartialOrd> Pair<T> {
fn show_larger(&self) {
if self.x >= self.y { println!("larger is x = {}", self.x); }
else { println!("larger is y = {}", self.y); }
}
}
trait Shout { fn shout(&self) -> String; }
impl<T: Display> Shout for T {
fn shout(&self) -> String { format!("{}!", self) }
}
fn main() {
let pair = Pair { x: 3, y: 9 };
pair.show_larger();
println!("{}", 42.shout());
println!("{}", "hi".shout());
}Key points
- A blanket impl is forever.
- Publish impl<T: Display> Loud for T once, and the rule is fixed.
- No one may ever add a special Loud impl for one Display type.
- Blanket impls are why coherence matters.
- The orphan rule plus the overlap ban let std make a promise.
- Display always brings ToString with it.
- And no rival impl exists anywhere.
- A blanket impl is a conditional impl that gives a trait to every type that meets the bound, all at once.
- A conditional impl provides methods only when its bounds hold.
In this example, Pair holds 3 and 9. i32 can be compared and printed. So show_larger exists. 3 is not at least 9. So it prints larger is y = 9. Below, Shout is given by one blanket impl to every Display type. So the number 42 can shout as 42!, and the text hi can shout as hi! — neither type needed its own impl block.
Note
Blanket impls are powerful and can clash.
If a type already has the trait another way, a new blanket impl over it will not compile.
Try it Yourself
Edit the code, then press Run.
Running is simulated · Rust Playground
Exercise
What is a blanket implementation?