Allow to seal blanket impls
tyranron opened this issue · comments
Synopsis
With the current implementation it's impossible to seal a trait having blanket implementations and foreign types. For example:
#[sealed]
pub trait AsSpan {
fn as_span(&self) -> Span;
}
impl AsSpan for Span { // foreign type, cannot place #[sealed]
fn as_span(&self) -> Self { *self }
}
impl<T: Spanned> AsSpan for &T { // blanket impl, cannot place #[sealed]
fn as_span(&self) -> Span { self.span() }
}
Proposed solution
Place #[sealed]
attribute on impl
blocks rather than on types:
#[sealed]
pub trait AsSpan {
fn as_span(&self) -> Span;
}
// expands to:
// pub trait AsSpan: private::Sealed {
// fn as_span(&self) -> Span;
// }
// mod private {
// pub trait Sealed {}
// }
#[sealed]
impl AsSpan for Span {
fn as_span(&self) -> Self { *self }
}
// expands to:
// impl AsSpan for Span { // foreign type, cannot place #[sealed]
// fn as_span(&self) -> Self { *self }
// }
// impl private::Sealed for Span {}
#[sealed]
impl<T: Spanned> AsSpan for &T {
fn as_span(&self) -> Span { self.span() }
}
// expands to:
// impl<T: Spanned> AsSpan for &T {
// fn as_span(&self) -> Span { self.span() }
// }
// impl<T: Spanned> private::Sealed for &T {}
More benefits
Also, having a flat trait signature like pub trait Sealed {}
imposes problems with coherence for more complicated traits. Consider the following example:
pub trait Set<V> {}
impl<T> Set<Option<T>> for T {}
impl<T> Set<Option<T>> for Option<T> {}
This compiles perfectly well. But once we seal it, we loose the coherence:
pub trait Set<V>: private::Sealed {}
mod private {
pub trait Sealed {}
}
impl<T> Set<Option<T>> for T {}
impl<T> private::Sealed for T {}
impl<T> Set<Option<T>> for Option<T> {}
impl<T> private::Sealed for Option<T> {}
To overcome this limitation, the private::Sealed
trait should fully follow the parametrization of the original trait:
pub trait Set<V>: private::Sealed<V> {}
mod private {
pub trait Sealed<V> {}
}
impl<T> Set<Option<T>> for T {}
impl<T> private::Sealed<Option<T>> for T {}
impl<T> Set<Option<T>> for Option<T> {}
impl<T> private::Sealed<Option<T>> for Option<T> {}
And we cannot do that by putting a #[sealed]
attribute on top of a type rather than an impl block, because we simply don't have parametrization info there.
@jmg-duarte if you're not against such redesign, I'd like to contribute this via PR in few days.
Closing in favor of #6