Skip to main content

closed_trait/
lib.rs

1#![doc = include_str!("../README.md")]
2#![no_std]
3
4pub use closed_trait_macros::{enumerate, sealed};
5
6/// A type that can be turned into the enum of its sealed trait.
7///
8/// [`enumerate`] implements this for every permitted type, and makes
9/// `Enumerable<AnyShape>` a supertrait of the sealed trait. Naming the enum in
10/// the bound is what lets a caller reach it through the trait alone:
11///
12/// ```
13/// use closed_trait::{enumerate, sealed};
14///
15/// pub struct Square { pub side: i32 }
16/// pub struct Circle { pub radius: i32 }
17///
18/// #[enumerate]
19/// #[sealed(Square, Circle)]
20/// pub trait Shape {}
21///
22/// impl Shape for Square {}
23/// impl Shape for Circle {}
24///
25/// /// Generic over the trait, yet able to match exhaustively.
26/// fn corners<S: Shape>(shape: S) -> u32 {
27///     match shape.into_enum() {
28///         AnyShape::Square(_) => 4,
29///         AnyShape::Circle(_) => 0,
30///     }
31/// }
32///
33/// fn main() {
34///     assert_eq!(corners(Square { side: 1 }), 4);
35///     assert_eq!(corners(Circle { radius: 1 }), 0);
36/// }
37/// ```
38///
39/// Note that `Enumerable` did not have to be imported above: the supertrait
40/// bound brings `into_enum` into scope through `S: Shape`. Calling it on a
41/// concrete type rather than a generic one does need the import.
42///
43/// The enum is a type *parameter* rather than an associated type so that one
44/// type can belong to several sealed traits at once, since an associated type could
45/// only be chosen once per implementor. The cost is that `into_enum` on a
46/// concrete type belonging to more than one needs the target spelled out, by
47/// annotation or turbofish. `From` sidesteps that, since the enum is named by
48/// the conversion itself.
49///
50/// `From` is implemented alongside it in the other direction, so
51/// `AnyShape::from(square)` and `square.into()` work too.
52pub trait Enumerable<Enum> {
53    /// Wraps `self` in the variant of `Enum` that holds this type.
54    fn into_enum(self) -> Enum;
55}
56
57/// A type that can lend itself to the *borrowing* enum of its sealed trait.
58///
59/// [`enumerate`] implements this for every permitted type and makes
60/// `for<'a> EnumerableRef<'a, AnyShapeRef<'a>>` a supertrait. The
61/// lifetime is a parameter of the trait rather than of the method, so the
62/// higher-ranked bound is nameable in the supertrait list, which is what lets
63/// a caller reach the enum from a plain `&S`:
64///
65/// ```
66/// use closed_trait::{enumerate, sealed};
67///
68/// pub struct Square { pub side: i32 }
69/// pub struct Circle { pub radius: i32 }
70///
71/// #[enumerate(match_any)]
72/// #[sealed(Square, Circle)]
73/// pub trait Shape {}
74///
75/// impl Shape for Square {}
76/// impl Shape for Circle {}
77///
78/// /// Takes a reference, yet still matches exhaustively.
79/// fn corners<S: Shape>(shape: &S) -> u32 {
80///     match shape.as_enum_ref() {
81///         AnyShapeRef::Square(_) => 4,
82///         AnyShapeRef::Circle(_) => 0,
83///     }
84/// }
85///
86/// fn main() {
87///     assert_eq!(corners(&Square { side: 1 }), 4);
88///     assert_eq!(corners(&Circle { radius: 1 }), 0);
89/// }
90/// ```
91///
92/// [`Enumerable`] cannot do this: `into_enum` takes `self`, so reaching the
93/// owned enum means owning the value. The borrowing enum is also the cheaper
94/// one to pass, being a pointer and a discriminant rather than as large as the
95/// biggest permitted type.
96pub trait EnumerableRef<'a, EnumRef> {
97    /// Wraps `&self` in the variant of `EnumRef` that holds this type.
98    fn as_enum_ref(&'a self) -> EnumRef;
99}
100
101/// A type that can lend itself *mutably* to the borrowing enum of its sealed
102/// trait.
103///
104/// The counterpart of [`EnumerableRef`], and reached from a `&mut S` the same
105/// way:
106///
107/// ```
108/// use closed_trait::{enumerate, sealed};
109///
110/// pub struct Square { pub side: i32 }
111/// pub struct Circle { pub radius: i32 }
112///
113/// #[enumerate]
114/// #[sealed(Square, Circle)]
115/// pub trait Shape {
116///     fn grow(&mut self);
117/// }
118///
119/// impl Shape for Square { fn grow(&mut self) { self.side += 1; } }
120/// impl Shape for Circle { fn grow(&mut self) { self.radius += 1; } }
121///
122/// fn grow_twice<S: Shape>(shape: &mut S) {
123///     match shape.as_enum_mut() {
124///         AnyShapeMut::Square(s) => { s.grow(); s.grow(); }
125///         AnyShapeMut::Circle(c) => { c.grow(); c.grow(); }
126///     }
127/// }
128///
129/// fn main() {
130///     let mut square = Square { side: 1 };
131///     grow_twice(&mut square);
132///     assert_eq!(square.side, 3);
133/// }
134/// ```
135///
136/// Unlike the shared enum this one is neither `Clone` nor `Copy`, a unique
137/// reference being neither.
138pub trait EnumerableMut<'a, EnumMut> {
139    /// Wraps `&mut self` in the variant of `EnumMut` that holds this type.
140    fn as_enum_mut(&'a mut self) -> EnumMut;
141}