jmg-duarte / sealed-rs Goto Github PK
View Code? Open in Web Editor NEWMacro for sealing traits and structures
Home Page: https://docs.rs/sealed/
License: Apache License 2.0
Macro for sealing traits and structures
Home Page: https://docs.rs/sealed/
License: Apache License 2.0
Currently, using #[sealed]
on an enum fails with expected struct or trait
. I don't see a reason this shouldn't work on enums too, besides not being implemented in the macro.
I recently read a very nice blog about sealed traits.
It talked about a few ways to seal not simply a trait, but specific methods that are part of the trait.
It is possible to seal a method, either for implementation, or also for use.
I am not sure if this would be a good addition to this crate, as it is quite niche, as well as somewhat different from what this crate is doing.
I think however, that it would make sense for the ecosystem that if this feature would be a thing, it would be in this crate, as it seems to me as a natural extension.
If others agree that it should be in here I'd be more than happy to make a PR for it, otherwise I'll make my own crate for it.
While using pub(crate) mod
allows to overcome nesting for impls, it also makes using #[sealed]
pointless in bounds of a single crate: imagine usage in application crate, where the trait is declared in some module, not other crate.
It would be nice to control the visibility via attribute argument:
#[sealed]
pub trait T {}
// expands to
mod __seal_t {}
#[sealed(pub(crate))]
pub trait T {}
// expands to
pub(crate) mod __seal_t {}
// And even follow an arbitrary visiblity:
// https://doc.rust-lang.org/reference/visibility-and-privacy.html
#[sealed(pub(in crate::very::long::path))]
pub trait T {}
// expands to
pub(in crate::very::long::path) mod __seal_t {}
So, for the nesting case (which is quite rare, AFAIK), an user should specify the visibility explicitly, while for the usage in non-library crate, the sealing will work well out-of-the-box.
This would be useful to avoid CI errors due to formatting
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() }
}
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 {}
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.
#[sealed::sealed]
pub trait Testing<'a> {}
error[E0106]: missing lifetime specifier
|
1 | #[sealed::sealed]
| ^ expected named lifetime parameter
|
= note: this error originates in the attribute macro `sealed::sealed` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider using the `'a` lifetime
|
1 | #<'a>[sealed::sealed]
| ~~~~~
For more information about this error, try `rustc --explain E0106`.
As it stands, the test for const-generics fails for version 1.51.0
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
error[E0277]: `[T; N]` is not an iterator
--> tests/pass/13-const-generics.rs:11:33
|
11 | IntoIterator::into_iter(arr).collect()
| ^^^
| |
| expected an implementor of trait `IntoIterator`
| help: consider borrowing here: `&arr`
|
= note: the trait bound `[T; N]: IntoIterator` is not satisfied
= note: required because of the requirements on the impl of `IntoIterator` for `[T; N]`
= note: required by `into_iter`
error[E0599]: the method `collect` exists for array `[T; N]`, but its trait bounds were not satisfied
--> tests/pass/13-const-generics.rs:11:38
|
11 | IntoIterator::into_iter(arr).collect()
| ^^^^^^^ method cannot be called on `[T; N]` due to unsatisfied trait bounds
|
= note: the following trait bounds were not satisfied:
`[T; N]: Iterator`
which is required by `&mut [T; N]: Iterator`
`[T]: Iterator`
which is required by `&mut [T]: Iterator`
┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈┈
Finding a simpler test that works would allow test runs for 1.50.0
While simple, the crate has no documentation.
This should be added, along with badges.
This is due to #[sealed]
attribute desugras into the hardcoded private
name of a module.
The name should be unique per trait and must be derivable from the trait's name. Something like:
#[sealed]
pub trait MyTrait {}
// expands to:
// pub trait MyTrait: __my_trait::Sealed {}
// mod __my_trait { pub trait Sealed {} }
The most clear example would be const generics which were made "stable" in 1.51.0, as far as I know the crate should be able to work with earlier Rust versions.
Thus, ideally, the test would only run from 1.51 forwards and all others would run normally.
Cross-ref to #14 which is related but not the same
A lot of the used actions are deprecated or throwing warnings
https://github.com/jmg-duarte/sealed-rs/actions/runs/4764661109
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.