Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Trait Design

Rule

Write small behavior-focused traits, document their contracts, and seal public traits when external implementations are not intended.

Why

Traits are extension contracts. Their required methods and semantics constrain implementors, object users, and future API evolution.

Do

  • Keep required methods cohesive and minimal.
  • Name traits after behavior or capability.
  • Document implementor guarantees, errors, blocking behavior, and concurrency expectations.
  • Build convenience methods from the required core as provided methods.
  • Use associated types when each implementor chooses a related type.
  • Use generic methods when each caller chooses the type for a call.
  • Keep bounds near the function that needs them.
  • Make traits object-safe when intended for dyn Trait; put generic provided methods behind Self: Sized.
  • Seal a public trait when callers may use it but only the crate may implement it.

Avoid

  • Do not create a trait merely to organize one concrete type’s methods.
  • Do not combine unrelated capabilities into one trait.
  • Do not add required methods casually to an open public trait.
  • Do not add blanket implementations that block plausible future impls.
  • Do not encode inheritance with supertraits unless every supertrait is a real contract.

Public API Notes

An unsealed public trait is a semver commitment to external implementors. See public API evolution for compatibility policy.

Example

#![allow(unused)]
fn main() {
pub trait Notifier {
    fn notify(&self, message: &Message) -> Result<(), NotifyError>;

    fn notify_text(&self, body: impl Into<String>) -> Result<(), NotifyError>
    where
        Self: Sized,
    {
        self.notify(&Message::new(body))
    }
}
}

Exceptions

  • Match a broader mature ecosystem trait when interoperability is the purpose.
  • Use a marker trait only for a real compile-time contract.
  • Leave a public trait open when downstream implementations are intentional.