F-pattern_types : Subsets enums using as
FWIW, the example as currently spelled in the OP
DidingasLushis:
#[derive(Debug, Error)] pub enum MyError { #[error("not found")] NotFound, #[error("invalid state")] InvalidState, #[error("permission denied")] PermissionDenied, } #[derive(Debug, Error)] pub enum MyErrorSubset { MyError::NotFound, MyError::PermissionDenied, }
seems like it's a combination of "enum variants are types" and "types as enum variants." The "pattern restricted subtype" spelling
scottmcm:
type MyErrorSubset = MyError is (MyError::NotFound | MyError::PermissionDenied);
feels to me like it would be the most commonly used option in a world with all options available, as it asks for the simple type restriction of MyError to the possible variants without any layout changes (thus true no-op conversion to MyError) and inheriting all (immutable) trait functionality automatically.
With pattern restricted subtypes, the "enum variant as type" MyError::NotFound would likely be spelled MyError is MyError::NotFound. But "types as enum variants" still gives the tuple struct equivalent for enum types where the variants are indexed instead of named, and while this is technically just sugar1], I believe it's sugar that would make "newtype variants" nicer to work with[[2].
In any case, pattern restricted types are stalled because they're difficult. We want to inherit all functionality from MyError to MyErrorSubset, but while &MyErrorSubset ⊆ &MyError, &mut MyError ⊆ &mut MyErrorSubSet, and also &MyError ⊆ &mut MyError and &MyErrorSubset ⊆ &mut MyErrorSubset, so it's a complicated mess, and internal mutability makes things even more complicated… knowing what associated functions and trait impls can be provided on the subset type is a very thorny problem, and it gets even more thorny once you start considering forwards compatibility with API evolution that we deem semver compatible.
But even given all of that, pattern restricted types are super useful, and I hope I get to see Rust gain a version of them within my career, even if it's massively restricted[3].
Whether it's just sugar or not depends on whether the type has sum type semantics (multiple variants using the same type are distinct) or union type semantics (multiple variants using the same type are unified). In the face of generics, I believe that sum type semantics are the consistent choice. ↩︎
In short,
enum EnumType { use VariantType }would be sugar forenum EnumType { 0(VariantType) }, which we'd likely write today asenum EnumType { Variant(VariantEnum) }. Pattern matching would use the index or, in the most ideal case, allow using type ascription syntax when that is unambiguous. ↩︎A version which only allows pattern restricted types for the field of newtype structs and requires
unsafefor accesses to the inner field (thus manual and unsafe forwarding of any traits) simply as a way to spell custom niches in user code would be wonderful, even without further niceties that it could theoretically enable. ↩︎
Discussion in the ATmosphere