๐Ÿฆ€ Functional Rust
๐ŸŽฌ Traits & Generics Shared behaviour, static vs dynamic dispatch, zero-cost polymorphism.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Traits define shared behaviour โ€” like interfaces but with default implementations

โ€ข Generics with trait bounds: fn process(item: T) โ€” monomorphized at compile time

โ€ข Static dispatch (impl Trait) = zero cost; dynamic dispatch (dyn Trait) = runtime flexibility via vtable

โ€ข Blanket implementations apply traits to all types matching a bound

โ€ข Associated types and supertraits enable complex type relationships

381: Blanket Implementations

Difficulty: 3 Level: Advanced Implement a trait for every type that satisfies a bound โ€” at once, forever.

The Problem This Solves

You've defined a trait, and you want it available on many types โ€” but you don't want to write `impl MyTrait for i32`, `impl MyTrait for String`, `impl MyTrait for f64` one by one. Worse, new types keep being added: yours, library types, user types. Maintaining a list of impls is futile. The standard library faces this constantly. `Iterator` has 70+ adapter methods โ€” they work on every iterator, whether it's a `Vec`, a file, a network stream, or something invented tomorrow. The library authors couldn't have written an impl for each. Instead they wrote one blanket impl: if you implement `Iterator`, you get all the adapter methods for free. Blanket implementations solve this by saying: "for all `T` satisfying bound `B`, here is how `T` implements `Trait`." One impl covers infinite types.

The Intuition

A blanket impl is just a generic `impl` where the type parameter is unconstrained except by trait bounds. Instead of `impl Foo for Bar`, you write `impl<T: Something> Foo for T`. That single declaration automatically covers every type โ€” present and future โ€” that implements `Something`. Think of it as a universal factory: the bounds are the inputs, the trait impl is the output, and the compiler runs the factory at compile time for every qualifying type it encounters.

How It Works in Rust

use std::fmt;

trait Summary {
 fn summarize(&self) -> String;
}

// Blanket impl: EVERY type that is Display automatically gets Summary
impl<T: fmt::Display> Summary for T {
 fn summarize(&self) -> String {
     format!("Summary: {}", self)  // we can call Display because T: Display
 }
}

// Now i32, f64, &str, String, etc. all have .summarize() โ€” no extra code needed
fn print_summary<T: Summary>(item: &T) {
 println!("{}", item.summarize());
}

fn main() {
 print_summary(&42);           // i32 gets Summary via blanket impl
 print_summary(&3.14f64);      // f64 too
 print_summary(&"hello");      // &str too
}
The compiler checks: does `i32: fmt::Display`? Yes. So `i32: Summary`. It generates the impl on demand โ€” there's no runtime cost. You can stack bounds: `impl<T: Display + Clone> MyTrait for T {}` means "only types that are both Display and Clone qualify."

What This Unlocks

Key Differences

ConceptOCamlRust
Blanket impl`MakeDescribable(P: Printable)` functor โ€” explicit per-module application`impl<T: Display> Trait for T` โ€” implicit, compiler-driven, applies globally
Open-world extensibilityNew type needs a new functor applicationNew type gets the impl automatically if it satisfies the bound
CoherenceMultiple functor applications can coexistOnly one blanket impl per (Trait, Type) pair โ€” compiler enforces uniqueness
Syntax`module M = MakeDescribable(P)``impl<T: Bound> Trait for T {}`
// Blanket implementations in Rust
use std::fmt;

trait Summary {
    fn summarize(&self) -> String;
}

// Blanket impl: anything that is Display also gets Summary
impl<T: fmt::Display> Summary for T {
    fn summarize(&self) -> String {
        format!("Summary: {}", self)
    }
}

// Another example: blanket impl for conversion
trait DoubleString {
    fn double_string(&self) -> String;
}

impl<T: fmt::Display> DoubleString for T {
    fn double_string(&self) -> String {
        let s = self.to_string();
        format!("{}{}", s, s)
    }
}

fn print_summary<T: Summary>(item: &T) {
    println!("{}", item.summarize());
}

fn main() {
    // i32 gets Summary via blanket impl (Display is implemented for i32)
    print_summary(&42);
    print_summary(&3.14f64);
    print_summary(&"hello world");

    println!("{}", 7u32.double_string());
    println!("{}", "abc".double_string());
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_blanket_summary() {
        assert_eq!(42i32.summarize(), "Summary: 42");
        assert_eq!("hi".summarize(), "Summary: hi");
    }

    #[test]
    fn test_blanket_double() {
        assert_eq!(5u8.double_string(), "55");
        assert_eq!("ab".double_string(), "abab");
    }
}
(* Blanket implementations simulated via functors in OCaml *)

module type Printable = sig
  type t
  val to_string : t -> string
end

module type Describable = sig
  type t
  val describe : t -> string
end

(* "Blanket impl": any Printable is also Describable *)
module MakeDescribable (P : Printable) : Describable with type t = P.t = struct
  type t = P.t
  let describe x = "Value: " ^ P.to_string x
end

module IntPrintable = struct
  type t = int
  let to_string = string_of_int
end

module FloatPrintable = struct
  type t = float
  let to_string = string_of_float
end

module IntDescribable = MakeDescribable(IntPrintable)
module FloatDescribable = MakeDescribable(FloatPrintable)

let () =
  Printf.printf "%s\n" (IntDescribable.describe 42);
  Printf.printf "%s\n" (FloatDescribable.describe 3.14)