Downstream crates may implement a non-public trait

Consider the following code:

trait A {}

impl<T: A> Iterator for T {
    type Item = ();
    fn next(&mut self) -> Option<Self::Item> {
        None
    }
}

Where the trait A is not exposed in any place as public.

The compiler says,

error[E0119]: conflicting implementations of trait `std::iter::Iterator` for type `&mut _`:
 --> src/lib.rs:3:1
  |
3 | impl<T: A> Iterator for T {
  | ^^^^^^^^^^^^^^^^^^^^^^^^^
  |
  = note: conflicting implementation in crate `core`:
          - impl<I> std::iter::Iterator for &mut I
            where I: std::iter::Iterator, I: ?Sized;
  = note: downstream crates may implement trait `A` for type `&mut _`

But we know no-one outside of this crate can implement A, since they don't have access to it.

Is this a bug? design flaw? something unconsidered?

We also have to remember that in case of pub(crate) trait, the compiler must analyze crate-wise in order to rule that the trait is not pub used somewhere. Edit: pub(crate) cannot be re-exported.

Rust doesn't support sealed traits.

Sealed traits? What's the point of them?

This is not what I'm talking about. The compiler afraid that someone outside of this crate will impl Iterator for some type T implementing A, causing impl collision. Unfortunately such thing won't happen, because outside of the crate we can't impl A.

A sealed trait is a trait that can't have be implemented by downstream crates. Because Rust doesn't have sealed traits, it doesn't take into account the fact that the trait is private when determining if a downstream implementation could conflict with a given implementation.

I also think making A public at some point now would be a breaking change which it shouldn't.

Maybe you can solve your problem by defining trait A: Iterator {} and then implementing A in terms of Iterator instead of the other way around?

IIUC, sealed traits was meant to prevent impl outside of the current crate. Rust doesn't support them.

But Rust doesn't need them for traits that are not accessible at all outside of the current crate. Those cannot be impled, too, but the syntax already exists.

Why is it shouldn't? The API is private and won't break consumer code. If you wish opening it to the public you have some work to do.

In fact, my trait already inherits from Iterator. But in special cases I want to auto-impl Iterator if you've implemented all methods of A. The trait bounds are like above.

Sorry, you are right. Since the error would show up in your crate when you make it public, it wouldn't break downstream.

After some digging into details, I've came across Sealed traits protect against downstream implementations. With my suggestion, this design pattern can prevent the need in Pre-RFC: Sealed traits: we will implement the pattern and automatically get the ability to impl as we want for free. One could even crate a procedural macro #[sealed] that automatically creates the private module and crate, bring the code closer to the RFC.

Rust is not programmed to ever believe it knows all of the types that implement a given trait, so there could be some unknown type that both implements Iterator and A. This is correct in the usual case of traits that are freely implementable, but occasionally causes problems (as here).

Sealed trait proposals are primarily about enabling the compiler to reason this way for some traits that are fully controlled by a single crate (by some definition of “fully controlled”). That generally involves preventing downstream implementations, but more as a side effect than the primary motivation.

I don’t follow internals discussions very closely, but my impression is that something along these lines might be developed some day but it’s not a current priority. There have certainly been a few proto-RFCs that should have good discussion about the benefits and drawbacks of the idea.

1 Like

Seems like this is already tracked at https://github.com/rust-lang/rust/issues/48869.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.