Blanket Implementations
A blanket implementation is a generic trait implementation of the form impl<T: Bound> Trait for T (or impl<T> Trait for &T) that implements a trait automatically for every type T that satisfies the specified bounds, rather than for a single named struct or enum.
Standard Library Blanket Implementations
Two of the most widely used trait implementations in Rust's standard library are blanket implementations:
1. ToString for Every T: Display
In alloc::string, the standard library defines:
// Conceptual definition in the standard library:
// impl<T: fmt::Display + ?Sized> ToString for T { ... }
Because of this blanket implementation, any type that implements std::fmt::Display automatically gains .to_string() without writing impl ToString for MyType.
2. Into<U> for Every T Where U: From<T>
In core::convert, the standard library defines:
// Conceptual definition in the standard library:
// impl<T, U> Into<U> for T where U: From<T> {
// fn into(self) -> U { U::from(self) }
// }
Whenever you implement From<Source> for Target, the blanket implementation automatically provides Into<Target> for Source.
Writing a Custom Blanket Implementation
You can define your own trait and provide a blanket implementation so every type satisfying an existing bound gains your trait's methods automatically:
use std::fmt::Debug;
trait Inspectable {
fn debug_type_and_value(&self) -> String;
}
// Blanket implementation for ALL types T that implement Debug:
impl<T: Debug> Inspectable for T {
fn debug_type_and_value(&self) -> String {
format!("{}: {:?}", std::any::type_name::<T>(), self)
}
}
#[derive(Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
// Primitives, slices, and custom structs all get `debug_type_and_value()` automatically:
println!("{}", 42_i32.debug_type_and_value());
println!("{}", [1, 2, 3].debug_type_and_value());
println!("{}", Point { x: 5, y: 9 }.debug_type_and_value());
}
i32: 42
[i32; 3]: [1, 2, 3]
blank::Point: Point { x: 5, y: 9 }
Forwarding Blanket Implementations for References (&T and &mut T)
Library authors frequently write blanket implementations for shared or mutable references (impl<T: MyTrait + ?Sized> MyTrait for &T) so generic functions that accept T: MyTrait by value also work seamlessly when callers pass a reference &MyType:
trait MetricSource {
fn sample(&self) -> f64;
}
// Forwarding blanket impl: if `T` is a `MetricSource`, then `&T` is also a `MetricSource`
impl<T: MetricSource + ?Sized> MetricSource for &T {
fn sample(&self) -> f64 {
(**self).sample()
}
}
struct CpuLoad(f64);
impl MetricSource for CpuLoad {
fn sample(&self) -> f64 {
self.0
}
}
fn record_metric<M: MetricSource>(source: M) {
println!("Recorded sample: {:.2}", source.sample());
}
fn main() {
let cpu = CpuLoad(0.73);
// Works by reference (&CpuLoad) without consuming `cpu`:
record_metric(&cpu);
// And `cpu` can still be passed by value afterward:
record_metric(cpu);
}
Recorded sample: 0.73
Recorded sample: 0.73
Coherence Restrictions on Blanket Implementations
Because impl<T: Bound> Trait for T covers every current and future type implementing Bound, two rules apply:
- Local trait requirement (
E0210): You can only write a blanket implementationimpl<T: ...> Trait for TifTraitis defined in your own crate. - No overlapping concrete overrides (
E0119): Once a crate definesimpl<T: Display> MyTrait for T, no otherimpl MyTrait for ConcreteTypecan be written for any type that implements (or might later implement)Display.