Skip to content

Week 6 - Traits, Coherence, and Monomorphization

6.1 Conceptual Core

  • A trait is a named set of capabilities. Implementing a trait for a type is a claim that the type satisfies a contract-the contract is partly enforced by the compiler and partly by the implementor's discipline (e.g., Hash and Eq consistency).
  • Coherence / orphan rule: an impl Trait for Type is allowed only if either Trait or Type is local to the current crate. This is what prevents "two crates impl the same trait for the same type" diamond conflicts.
  • Monomorphization: every distinct type substitution at a generic call site produces a fresh compiled function in the binary. Vec<u8>::push and Vec<i32>::push are two different symbols. This is the dual edge of the language: zero-cost abstraction in exchange for binary bloat and compile time.

6.2 Mechanical Detail

  • Static dispatch (impl Trait, generics) vs dynamic dispatch (dyn Trait). The vtable layout of dyn Trait: a fat pointer (data pointer + vtable pointer), where the vtable contains the destructor, size, alignment, and the trait method pointers. Inspect a real one with cargo asm.
  • Object safety / dyn compatibility: a trait is dyn-compatible if every method has a Self: Sized bound or takes self by reference and does not return Self or use Self in generic position. The 2024 edition formalized "dyn-compatible" terminology-use it.
  • Sealed traits: declare a private supertrait in a private module to prevent downstream impls. Used in std::error::Error historically, in tokio::io::AsyncRead extensions, and in any API where future-compatibility demands closed extension.
  • Auto traits (Send, Sync, Unpin, UnwindSafe, RefUnwindSafe): structural, opt-out via negative impl on nightly or PhantomData<*const ()> on stable.

6.3 Lab-"Bloat Forensics"

  1. Write a generic function fn process<T: Display>(items: &[T]) -> String that formats and concatenates. Instantiate it with five distinct types in a binary.
  2. Run cargo bloat --release --filter process and confirm there are five symbols.
  3. Refactor to a dyn Display version (&[&dyn Display]). Re-run cargo bloat. Document the binary-size delta and the codegen tradeoff.
  4. Now read the disassembly of the dyn version with cargo asm and identify the indirect call.

6.4 Idiomatic & Clippy Drill

  • clippy::needless_pass_by_value, clippy::trait_duplication_in_bounds, clippy::implicit_hasher. The last is subtle-explain why leaking RandomState into a public API is a SemVer hazard.

6.5 Production Hardening Slice

  • Configure [profile.release.package."*"] codegen-units = 1 for maximum cross-function inlining, while keeping [profile.dev] codegen-units = 256 for fast iteration. Document the build-time delta.

Comments