4 ms·
I really enjoy Rust's enums; but I feel that that actually their integration could be even better. TypeScript's flow typing or Rust's work on pattern types [0]
by demurgos 3y ago
I really enjoy Rust's enums; but I feel that that actually their integration could be even better. TypeScript's flow typing or Rust's work on pattern types [0] are better version of what we have currently.
The main issue is that it's very hard to propagate information found through matching .
[0]: https://github.com/rust-lang/rust/pull/120131 https://github.com/rust-lang/rust/pull/120131
- dwattttt 3y agoCan't you use @ to bind any part of the match you want to use? https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#-bindings https://doc.rust-lang.org/book/ch18-03-pattern-syntax.html#-...
- demurgos 3y agoWhat I want is something similar to the switch from lexical to non-lexical lifetimes; but for enums and pattern matching. `@` is a construct depending on lexical scoping In following code, it would be nice if the second match could use the early return from the first match to avoid extra checks. It would also be nice if it worked across function boundaries. It would help removing boilerplate conversions or some `unreachable!()` branches. enum Auth { Guest, User(u32), Bot(u32) } fn check_permissions(Auth &auth) -> Result<Permission, PermissionError> { match auth { Auth::Guest => return PermissionError::Unauthorized, Auth::User(_) | Auth::Bot(_) => { ... } // ... match auth { Auth::User(_) => {...} Auth::Bot(_) => {...} // At this point the compiler should know that `Auth::Guest` is not possible }
- rphln 3y agoOften I wish that: enum Auth { Guest, User(u32), Bot(u32) } was just syntactic sugar for struct Auth::Guest; struct Auth::User(u32); struct Auth::Bot(u32); type Auth = Auth::Guest | Auth::User | Auth::Bot; where `|` denotes an anonymous union. Using enums feel very clunky when you start needing to work with subsets of them.
- demurgos 3y agoI 100% agree. I tried a few workarounds to get the same result, but it quickly gets out of hand. The two main solutions I'm using is to define the individual variants as in your post, and then one enum per union I care about; even with macros it gets very verbose. There's also the problem that it requires explicit conversions (so it's not zero cost). As an alternative, I define a generic enum where each variant corresponds to a generic param and then you can set it to an inhabited type (such as Infallible) when the variant is not possible. Converting is not zero-cost again. You can retrieve the zero cost manually, but then you need to deal with discriminators yourself / the compiler can't help you there. The pattern type work I mentioned earlier would bring us very close to what you have at the end: enum Auth { Guest, User(u32), Bot(u32) } type FullAuth = pattern_type!(Auth, Auth::Guest | Auth::User(_) | Auth::Bot(_)); // useless, but works type IdAuth = pattern_type!(Auth, Auth::User(_) | Auth::Bot(_)); The full enum still needs to be defined. In particular, it serves as the basis the in-memory representation. All the subset can be handled by the compiler though.