Skip to content

rustdoc: Document whether traits are sealed or not #119280

Closed
@obi1kenobi

Description

@obi1kenobi

A trait is considered sealed if it can only be implemented in the crate that defined it. This obviously has important semver implications. Many changes that are breaking and semver-major on unsealed traits are completely fine and non-breaking on sealed traits:

  • adding trait functions, associated types, or constants, even if they don't have assigned defaults
  • adding supertraits
  • removing unsafe from a trait's function

Determining whether a trait is sealed is complex, since there are many ways to seal a trait -- this blog post digs into the subject. I believe the following to be an exhaustive list:

  • if the trait is pub-in-priv and not publicly re-exported
  • if the trait has a sealed supertrait (sealed in any way, not just pub-in-priv) that is defined in the same crate
  • if the trait has a "sealed method" without a default impl, in any of the ways that allow sealing a method:
    • a pub-in-priv type as a parameter or return type, including as a &dyn Trait (described here)
    • a sealed trait from the same crate used as a bound (described here — even if the bound is on a type that does not itself appear in the parameters or return type) this turned out to be incorrect due to possible refinement
  • if the trait has an associated type with a sealed trait from the same crate as a bound incorrect due to possible refinement
  • if the trait has an associated const of a pub-in-priv type that doesn't have a default incorrect due to possible refinement

Additionally, if the trait in question has a blanket impl, it is sealed if and only if the blanket impl has a bound on a trait that is itself sealed and defined in the same crate.

All the "from the same crate" qualifiers are important because without them, the trait becomes "sealed with respect to the crate where the other trait is defined" and only possible to implement in that crate.

As you can see, it's quite difficult to determine whether a trait is sealed or not when all the compiler's machinery is not present 😅

While RFC 3323 describes restrictions syntax that would add native support for sealed traits, I personally do not believe this is a replacement for implementing the rules above. It's unclear whether the RFC will be accepted, when it will become implemented and generally available, and when users of existing ways to seal traits will migrate to them. Since sealed traits by definition are meant for crate-internal implementation only, I don't think there will be strong pressure to change code that already works and I expect a very long migration period.

If tools such as cargo-semver-checks are only able to detect restrictions-based trait sealing, that means we'd have to make a no-win choice:

  • either we don't implement a dozen lints that require precise knowledge of whether the trait is sealed or not, missing out on semver breakage we could have caught
  • or we implement the lints anyway and report tons of false-positives when traits sealed with these "traditional methods" have non-breaking changes, frustrating maintainers to the point where I think they'd justifiably stop using the tool.

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-feature-requestCategory: A feature request, i.e: not implemented / a PR.E-hardCall for participation: Hard difficulty. Experience needed to fix: A lot.T-rustdocRelevant to the rustdoc team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions