jmg-duarte / sealed-rs

Macro for sealing traits and structures

Home Page:https://docs.rs/sealed/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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 {}

Playground

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> {}

Playground

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> {}

Playground

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