๐Ÿฆ€ 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

387: Sealed Trait Pattern

Difficulty: 3 Level: Advanced Prevent external crates from implementing your trait โ€” lock down your abstraction boundary.

The Problem This Solves

You design a trait that must only be implemented by types in your crate. Maybe it's a token trait (`Sealed`) used in your public API, and external implementations would break your invariants. Maybe it's a state machine trait where only your crate's types are valid states. Maybe you want the freedom to add methods to the trait later without breaking semver โ€” which you can't do with a public trait anyone might implement. Rust doesn't have a built-in `sealed` keyword, but the pattern is easy to implement: put a hidden supertrait in a private module. External crates can see your public trait but can't implement the private supertrait, so they can't implement the public trait either. This pattern is widely used in the Rust ecosystem: `tokio`, `futures`, and `serde` all use it to mark traits as not-meant-for-external-implementation.

The Intuition

The trick is that `impl MyPublicTrait for ExternalType` requires `impl private::Sealed for ExternalType` (because `MyPublicTrait: private::Sealed`). But `private::Sealed` is not accessible outside your crate โ€” the module is private. So the impl block can't compile. The user sees your public trait and can call its methods, but they can't add new implementations. This is Rust's module system doing the work, not a language feature. It's a convention, not a hard keyword โ€” but it's effective and idiomatic.

How It Works in Rust

// In your crate: lib.rs

mod private {
 // Not pub โ€” invisible outside this crate
 pub trait Sealed {}
}

// Public trait โ€” requires the private supertrait
pub trait Validated: private::Sealed {
 fn value(&self) -> &str;
}

// Your crate's types can implement Sealed (they're in the same crate)
pub struct Email(String);
pub struct Url(String);

impl private::Sealed for Email {}
impl private::Sealed for Url {}

impl Validated for Email {
 fn value(&self) -> &str { &self.0 }
}

impl Validated for Url {
 fn value(&self) -> &str { &self.0 }
}

// External crate trying to implement Validated:
// impl Validated for ExternalType { ... }
// ERROR: trait `private::Sealed` is not accessible
Users of your crate can write `fn process(v: &dyn Validated)` and call `v.value()` โ€” they just can't add new implementations.

What This Unlocks

Key Differences

ConceptOCamlRust
Module-private type`module M : sig type t end` (abstract type)`mod private { pub trait Sealed {} }`
Prevent external implAbstract module signaturesSealed trait via private supertrait
Closed set of typesVariant types (closed by design)Sealed trait (open syntax, closed semantics)
Adding methods safelyAdding to module signatureAdd to sealed trait โ€” no external impls to break
// Sealed trait pattern in Rust

// Private module to hold the seal
mod private {
    pub trait Sealed {}
}

// Public trait requires the private Sealed supertrait
// External code cannot implement this trait because they can't implement Sealed
pub trait Token: private::Sealed {
    fn value(&self) -> String;
    fn token_type(&self) -> &'static str;
}

pub struct Identifier(pub String);
pub struct Number(pub i64);
pub struct Punctuation(pub char);

// Only types in our crate can implement Sealed
impl private::Sealed for Identifier {}
impl private::Sealed for Number {}
impl private::Sealed for Punctuation {}

impl Token for Identifier {
    fn value(&self) -> String { self.0.clone() }
    fn token_type(&self) -> &'static str { "identifier" }
}

impl Token for Number {
    fn value(&self) -> String { self.0.to_string() }
    fn token_type(&self) -> &'static str { "number" }
}

impl Token for Punctuation {
    fn value(&self) -> String { self.0.to_string() }
    fn token_type(&self) -> &'static str { "punctuation" }
}

fn describe(token: &dyn Token) {
    println!("{}: {}", token.token_type(), token.value());
}

fn main() {
    let tokens: Vec<Box<dyn Token>> = vec![
        Box::new(Identifier("variable".to_string())),
        Box::new(Number(42)),
        Box::new(Punctuation(';')),
    ];

    for tok in &tokens {
        describe(tok.as_ref());
    }

    // Cannot implement Token for an external type:
    // struct MyToken;
    // impl private::Sealed for MyToken {} // ERROR: private
    // impl Token for MyToken {}            // ERROR: Sealed not impl'd
}

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

    #[test]
    fn test_sealed_token() {
        let id = Identifier("foo".to_string());
        assert_eq!(id.token_type(), "identifier");
        assert_eq!(id.value(), "foo");
    }

    #[test]
    fn test_number_token() {
        let n = Number(-99);
        assert_eq!(n.token_type(), "number");
        assert_eq!(n.value(), "-99");
    }
}
(* Sealed trait pattern in OCaml via module privacy *)

(* Closed sum type is the OCaml way to achieve sealed-ness *)
type token =
  | Identifier of string
  | Number of int
  | Punctuation of char
  (* external code cannot add new variants *)

let describe_token = function
  | Identifier s -> Printf.printf "Identifier: %s\n" s
  | Number n -> Printf.printf "Number: %d\n" n
  | Punctuation c -> Printf.printf "Punctuation: %c\n" c

(* Exhaustive pattern matching guaranteed *)
let is_value = function
  | Identifier _ | Number _ -> true
  | Punctuation _ -> false

let () =
  let tokens = [Identifier "x"; Number 42; Punctuation ';'] in
  List.iter describe_token tokens;
  List.iter (fun t ->
    Printf.printf "  is_value: %b\n" (is_value t)
  ) tokens