top | item 39852532

(no title)

rphln | 1 year ago

Often 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.

discuss

order

demurgos|1 year ago

I 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.