Associated Types and Advanced Traits
Blanket Implementations
A blanket implementation is a generic impl block that implements a trait for all types T (or all types matching a structural pattern like &T) that satisfy a specified set of trait bounds.
Anatomy of a Blanket Implementation
Instead of naming a concrete type after for, a blanket implementation introduces a type parameter on impl<T> and implements the trait for T:
impl<T: Bound> TargetTrait for T {
▲ ^^^^^ ^^^^^^^^^^^ ^
│ │ │ └── implemented for every matching type T
│ │ └───────────── the trait being implemented
│ └─────────────────────── prerequisite bound on T
└──────────────────────────── generic type parameter declaration
use std::fmt::Display;
trait JsonScalar {
fn to_json_string(&self) -> String;
}
// Blanket implementation for every type T that implements Display:
impl<T: Display> JsonScalar for T {
fn to_json_string(&self) -> String {
format!("\"{self}\"")
}
}
fn main() {
println!("Integer: {}", 42.to_json_string());
println!("Boolean: {}", true.to_json_string());
println!("Str: {}", "hello".to_json_string());
}
Integer: "42"
Boolean: "true"
Str: "hello"
Key Blanket Implementations in the Standard Library
| Blanket Implementation | Provided By | What It Unlocks |
|---|---|---|
impl<T: Display + ?Sized> ToString for T |
alloc::string |
.to_string() on any type that implements Display |
impl<T, U> Into<U> for T where U: From<T> |
core::convert |
.into() for every From implementation |
impl<T, U> TryInto<U> for T where U: TryFrom<T> |
core::convert |
.try_into() for every TryFrom implementation |
impl<I: Iterator> IntoIterator for I |
core::iter |
Using any Iterator directly in a for loop |
impl<T: ?Sized> Borrow<T> for T |
core::borrow |
Every type borrows as itself (&T) |
Forwarding Implementations Over References (&T and &mut T)
A related pattern implements a trait for &T or &mut T whenever T implements that trait, allowing generic functions to accept borrowed references by value without requiring callers to dereference:
trait MetricSource {
fn sample(&self) -> f64;
}
// Forwarding blanket impl for shared references `&T`:
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(src: impl MetricSource) {
println!("Sampled value: {:.2}", src.sample());
}
fn main() {
let cpu = CpuLoad(0.73);
record(&cpu); // Works with &CpuLoad via the forwarding blanket impl
record(cpu); // Also works with owned CpuLoad
}
Sampled value: 0.73
Sampled value: 0.73
Coherence Restrictions on Blanket Implementations
Because of the orphan and overlap rules:
- You can only write an uncovered blanket implementation
impl<T: Bound> MyTrait for Tfor a traitMyTraitdefined in your own crate. Attemptingimpl<T: MyBound> ForeignTrait for Tfails withE0210. - Once a trait has an open blanket implementation
impl<T: Bound> MyTrait for T, no otherimpl MyTrait for ConcreteTypecan be added in the same or downstream crates, becauseConcreteTypecould implementBoundnow or in the future (E0119).