Sealing, in programming, usually means that some API (mostly public) cannot be inherited, extended or implemented outside its definition place. For example, a sealed class or interface in Kotlin cannot be inherited or implemented outside the library where it's defined. In Rust, this idiom may be applied to traits.
Sealed trait is a publicly accessible trait, which cannot be implemented outside its definition place (module or crate, depending on the visibility of this trait).
/// This trait is sealed and cannot be implemented for types outside this crate. pub trait TheTrait: private::Sealed { // Zero or more methods that the user is allowed to call. fn ...(); // Zero or more private methods, not allowed for user to call. #[doc(hidden)] fn ...(); } // Implement for some types. impl TheTrait for usize { /* ... */ } mod private { pub trait Sealed {} // Implement for those same types, but no others. impl Sealed for usize {} }The empty private
Sealed
supertrait cannot be named by downstream crates, so we are guaranteed that implementations ofSealed
(and thereforeTheTrait
) only exist in the current crate.
This is the most common way to seal a trait. The boilerplate could be completely cut off by using a sealed
crate, providing a convenient macro to generate the one:
use sealed::sealed;
#[sealed]
pub trait TheTrait {}
#[sealed]
impl TheTrait for usize {}
However, there are alternative ways to seal a trait via its method signature, or even seal it partially.
The main purpose of sealing a trait is, of course, future-proofing of APIs.
We are free to add methods to
TheTrait
in a non-breaking release even though that would ordinarily be a breaking change for traits that are not sealed. Also we are free to change the signature of methods that are not publicly documented.
It's important to note that trait sealing fully relies on tricking over visibility rules (using a public supertrait or type, which name is not publicly exported), and so, has no impact on the type system semantics (a sealed public trait is just a regular public trait from the type system perspective). In theory, sealing a trait should affect its coherence, by relaxing its strictness for the use-cases which can never happen with a sealed trait. However, that would require a special support by compiler, which seems not gonna happen in the near future.
For better understanding traits sealing, its design and use-cases, read through the following articles:
- Rust API Guidelines: 10. Future proofing: Sealed traits protect against downstream implementations (C-SEALED)
- Predrag Gruevski: A definitive guide to sealed traits in Rust
- Jack Wrenn: Private Methods on a Public Trait
- Official
sealed
crate docs
Estimated time: 1 day
Seal the traits defined in this task's crate in the following way:
- Make the
MyIteratorExt
trait fully sealed. Do it manually, using thesealed
crate or a similar one is not allowed. - Make the
MyError
trait partially sealed. Only seal the method marked with#[doc(hidden)]
attribute. - Sealing should work on both module level (disallowing to implement the sealed trait or the sealed method in the root module of the crate or any other module outside the one where the traits are defined, prove it by providing commented implementations in the root module of the crate, which doesn't compile due to the seal, if uncommented) and crate level (prove it by creating documentation tests which doesn't compile due to the seal).
After completing everything above, you should be able to answer (and understand why) the following questions: