Why does sealed trait require a module?

// lib.rs
pub trait A: Private { }
trait Private { }

pub trait B: private::Private { }
mod private {
    pub trait Private { }
}

In this piece of code, how A is written is not recommended but B is recommended.

cargo building this warns about the trait A but not the trait B:

warning: trait `Private` is more private than the item `A`

How B is written is also recommended as "the sealed trait" pattern in Rust API Guidelines.

My question is, why is this the case? Both have the same effect of preventing A or B from being implemented outside of the crate/module, and keeping the Private trait's interfaces private. At the same time, B is obviously more verbose and less intuitive (I would not think of using the B pattern or think of that B will not trigger the warning that A does without getting to the API Guidelines). Yet B is the recommended way.

This doesn't compile until Rust 1.74.[1]

pub trait A: Private { }
trait Private { }

And at this point the sealed-via-module pattern is relatively established, whereas the above is still considered to more likely just be an accident... I guess. I don't know if that means one is really recommended over the other or not (but would be interested in seeing a citation).

Ideally we'll some day get some sort of first-class sealing that (e.g.) coherence takes into account, but that's presumably not coming any time soon.


  1. tip of the hat â†Šī¸Ž

1 Like

Well, 3323-restrictions - The Rust RFC Book is merged, so we're part of the way to first-class sealing (albeit not stably).

3 Likes

Revisiting the implementation in the coming weeks after I return home! :blush:

1 Like

So when I said B was recommended, I was just refering to the facts that it's not warned against like A, and that Rust API Guidelines suggests the sealed trait pattern.

1 Like