I don't think type 3 wants to be a trait, especially if it wants the duck typed behavior you demonstrate. Instead, it wants some sort of static type set, where you accept any member of the type set and can use any functionality provided by all types in the set. Basically, a shortcut for monomorphizing over a known set of types. Naturally, this would be incompatible with dynamic dispatch. I'd suggest maybe static trait Q { u32, i32 }
as a working name for the feature idea (or maybe enum trait
is better?), but while related to sealed traits, due to the implications on type checking / method lookup, it's a much different feature.
But I have a fourth kind/usage of sealed traits: type three, but adding new implementations for non-downstream types is fine. Basically, going back to my assertion that the coherence implications is just impl<T in ::downstream> !Q for T
.
An example use case for this would be FnPtr
in std. This would allow std to have
#[sealed(for coherence)]
#[unstable = "implementation detail"]
trait FnPtr {
fn as_addr(this: Self) -> usize;
}
impl_fn_ptr! {
extern
"Rust", "C", "system",
"C-unwind", "system-unwind",
// etc
fn(A, B, C, D, E, F, G, H, I, J, K, L) -> Ret
}
impl<F: FnPtr> Debug for F { … }
impl<F: FnPtr> fmt::Function for F { … }
impl<F: FnPtr> PartialEq for F { … }
impl<F: FnPtr> Eq for F { … }
// etc
and macro copy/paste a single sealed trait implementation over the function pointer types which is used to blanket implement the traits available for function pointers. Without the coherence guarantee that downstream types won't be covered by this blanket implementation, they cannot implement any of the blanket implemented traits. However, adding new implementers which are not downstream of std is perfectly fine.
I'd argue that treating type 2 as "type 4" is also fine... but what type 2 actually wants imho is unsafe trait
, where the unsafe requirement for implementing is don't.
I'll also argue that the case of "#[non_exhaustive] trait
is similar to structs in that it could be served by a private item, but having a way to state the intent directly is also a good idea. (#[non_exhaustive]
is an interesting way to spell it, since the first thing I think of is the enum
meaning and not the struct
one, and trait
already is an open-world set compares to enum
's closed-world set.)