A blanket implementation implements a trait for every type that satisfies
some bound, all at once, rather than one type at a time — for instance,
impl<T: Display> ToString for T gives every Display type ToString
for free, in a single impl block.
This is how, for example, every type implementing Display automatically
gets .to_string() in the standard library — the blanket impl covers the
entire (open-ended, unbounded) set of types satisfying Display, present
and future, without the standard library needing to know about any of
them individually.
Blanket impls are a powerful decoupling tool: a trait author can provide
functionality for "any type with property X" without ever enumerating
which types that includes, and library consumers get the functionality
automatically the moment their own type satisfies the bound — no explicit
opt-in impl required on their part. The tradeoff is that blanket impls
interact with the orphan rule in
restrictive ways (only the crate defining the trait can write a blanket
impl for it), specifically to prevent two different crates from
providing conflicting blanket impls for the same trait.
Basic usage example
trait Describe {
fn describe(&self) -> String;
}
impl<T: std::fmt::Display> Describe for T { // <- one impl covers every Display type at once
fn describe(&self) -> String {
format!("value: {self}")
}
}
println!("{}", 5.describe());
Restriction: only the crate that defines Describe may write this
blanket impl — the orphan rule forbids a downstream crate from
blanket-implementing someone else's trait for someone else's types.
Best practices & deeper information
Scenario
Writing generic code
A blanket impl can add a convenience method to every type satisfying a
bound at once, instead of asking each type to opt in individually —
useful when the behavior follows purely from the bound itself.
trait Clamp {
fn clamp_to(self, lo: Self, hi: Self) -> Self;
}
impl<T: PartialOrd> Clamp for T { // <- one impl covers every PartialOrd type at once
fn clamp_to(self, lo: Self, hi: Self) -> Self {
if self < lo { lo } else if self > hi { hi } else { self }
}
}
let level = 42.clamp_to(0, 100); // works for i32, f64, or any other PartialOrd type
Why this way: this only works because PartialOrd is a
trait bound the compiler can check at every call site
— see Traits for how impl Trait for Type is what makes an
implementation exist in the first place, here applied to every type
satisfying the bound rather than one named type.
Explanation
Embedded support: Full
The mechanism is identical under #![no_std] — a blanket impl is resolved
entirely at compile time, so it has no dependency on std or an
allocator either way. Where blanket impls earn their keep in embedded
Rust specifically is on top of embedded-hal's traits: because
embedded-hal defines narrow, minimal traits like OutputPin (see
trait's embedded explanation for the
fuller embedded-hal story), a crate can add a convenience extension trait
— blanket-implemented for every type that already implements OutputPin
— and every vendor's pin type gets the convenience method automatically,
without the extension-trait author ever naming a single concrete chip's
pin type. The orphan rule still applies exactly as in hosted code: only
the crate defining the extension trait may write the blanket impl, so
this pattern lives in a small helper crate that defines its own trait
rather than trying to blanket-impl embedded-hal's traits themselves for
foreign types.
Basic usage example
trait OutputPin {
fn set_high(&mut self);
fn set_low(&mut self);
}
trait PulseExt {
fn pulse(&mut self);
}
impl<P: OutputPin> PulseExt for P { // <- one impl covers every OutputPin type at once
fn pulse(&mut self) {
self.set_high();
self.set_low();
}
}
Best practices & deeper information
Scenario
Writing generic code
A helper crate wants to give every embedded-hal-compatible GPIO pin a
pulse convenience method without asking each vendor's HAL to implement
a new trait one type at a time.
trait OutputPin {
fn set_high(&mut self);
fn set_low(&mut self);
}
trait PulseExt: OutputPin {
fn pulse_n(&mut self, times: u8) { // <- default body: only needs OutputPin's two primitives
for _ in 0..times {
self.set_high();
self.set_low();
}
}
}
impl<P: OutputPin> PulseExt for P {} // <- blanket impl: every OutputPin implementer gets pulse_n for free
struct Gpio7;
impl OutputPin for Gpio7 {
fn set_high(&mut self) {}
fn set_low(&mut self) {}
}
Gpio7.pulse_n(3);
Why this way: the blanket impl covers the open-ended set of pin types
across every vendor's HAL, present and future, so PulseExt needs no
knowledge of which concrete pin types exist — the same decoupling
trait's embedded explanation describes
for embedded-hal itself, applied here to an add-on convenience method
rather than the hardware contract.
Scenario
Designing a public API
A crate offering ergonomics on top of embedded-hal traits must define
its own extension trait to blanket-impl, rather than attempting to
blanket-impl embedded-hal's own traits for foreign pin types.
// AVOID: blanket-impling a foreign trait for a foreign bound — the orphan
// rule forbids this outright, so it doesn't compile:
// impl<P: SomeVendorsPinTrait> embedded_hal::digital::OutputPin for P { ... }
// PREFER: define a new local trait and blanket-impl that instead
trait DebouncedRead {
fn read_debounced(&self) -> bool;
}
trait InputPin {
fn is_high(&self) -> bool;
}
impl<P: InputPin> DebouncedRead for P { // <- local trait, blanket-implemented: orphan rule is satisfied
fn read_debounced(&self) -> bool {
self.is_high() // a real impl would sample more than once
}
}
Why this way: the orphan rule requires the crate writing a blanket
impl to own either the trait or the type, so an ergonomics crate built on
embedded-hal always introduces its own trait as the blanket-impl target
— attempting to blanket-impl embedded-hal's traits directly for
"any foreign pin type" is exactly the conflict the orphan
rule exists to prevent.