Advanced Generics and Type-Level Programming

Sealed Traits

A sealed trait is a pub trait that downstream users can use as a trait bound or call methods on, but cannot implement for their own types outside the defining crate. Sealing a trait preserves the library author's freedom to add new required methods or associated types in minor releases without breaking semver compatibility, and guarantees that a closed set of type-state or marker implementations remains exhaustive.

How the Supertrait Sealing Pattern Works

Rust does not have a built-in sealed keyword. Instead, a trait is sealed by making it require a supertrait defined inside a private module (mod private { pub trait Sealed {} }):

Crate Root (`pub trait BytePrimitive: private::Sealed`)
  |
  +-- Public to external callers:
  |     `pub trait BytePrimitive` (External crates CAN bound `T: BytePrimitive`)
  |
  +-- Private submodule (`mod private` — NOT exported with `pub mod`!):
        `pub trait Sealed {}`     (External crates CANNOT name `private::Sealed`,
                                   so they CANNOT write `impl Sealed for MyType`!)

Because implementing BytePrimitive requires also implementing its supertrait private::Sealed (E0277), and mod private is inaccessible outside the crate, only types inside the defining crate can implement BytePrimitive.

mod private {
    pub trait Sealed {}
}

/// A trait that external callers can use in generic bounds,
/// but cannot implement for new types outside this module.
pub trait EndianPrimitive: private::Sealed + Copy {
    fn to_network_bytes(self) -> Vec<u8>;
}

impl private::Sealed for u16 {}
impl EndianPrimitive for u16 {
    fn to_network_bytes(self) -> Vec<u8> {
        self.to_be_bytes().to_vec()
    }
}

impl private::Sealed for u32 {}
impl EndianPrimitive for u32 {
    fn to_network_bytes(self) -> Vec<u8> {
        self.to_be_bytes().to_vec()
    }
}

pub fn encode_value<T: EndianPrimitive>(val: T) -> Vec<u8> {
    val.to_network_bytes()
}

fn main() {
    println!("u16: {:02x?}", encode_value(0x1234_u16));
    println!("u32: {:02x?}", encode_value(0x1234_5678_u32));
}
u16: [12, 34]
u32: [12, 34, 56, 78]

Method-Level Sealing with a Private Token Parameter

An alternative way to seal individual methods (or the entire trait without a supertrait) is requiring a private private::Token parameter on a trait method. External crates cannot construct or name private::Token, so they cannot write a valid method implementation:

mod internal {
    pub struct SealToken;
}

pub trait StateMarker {
    #[doc(hidden)]
    fn _seal(token: internal::SealToken);
}

pub struct OpenState;
impl StateMarker for OpenState {
    fn _seal(_: internal::SealToken) {}
}

fn verify_state<S: StateMarker>() {
    S::_seal(internal::SealToken);
    println!("verified sealed state marker");
}

fn main() {
    verify_state::<OpenState>();
}
verified sealed state marker

When to Seal a Trait

Use Case Why Sealing Helps
Type-state markers (trait ConnectionState: Sealed) Guarantees callers cannot invent fake states like struct SpoofedAuthenticated;
Overloaded parameter traits (str::Pattern, SliceIndex) Allows standard/library functions to accept &str, char, or [char; N] via one trait without committing to public stabilization of internal methods
Evolving public traits Adding a new non-default method to an unsealed pub trait is a major semver-breaking change; adding a method to a sealed trait is non-breaking