I have an enum representing various actions that can be taken. However, some of its variants only makes sense in certain contexts, known at compile-time. To limit access to certain variants, I would like to add a const generic parameter to my enum like in the code below.
enum Action<const COND1: bool, const COND2: bool> {
A,
B,
C<enable if COND1>,
D<enable if COND2>,
}
let action1 = Action<false, false>::A;
match action1 {
Action<false, false>::A => {}
Action<false, false>::B => {}
}
let action2 = Action<true, false>::A;
match action2 {
Action<true, false>::A => {}
Action<true, false>::B => {}
Action<true, false>::C => {}
}
let action3 = Action<false, true>::A;
match action3 {
Action<false, true>::A => {}
Action<false, true>::B => {}
Action<false, true>::D => {}
}
Is this possible to achieve in rust?
I could create a separate enum for each combination, but that would result in a lot of duplicated code.
The never type can be used to "erase" variants that use a generic type, but this usage is not yet stabilized. On nightly:
#![feature(never_type)]
enum Action<C, D> {
A,
B,
C(C),
D(D),
}
You can use the ()
type to enable a variant and the !
type to disable it, so Action<!, ()>
would make the C
variant uninhabitable; matching on such a type would therefore not require the C
variant be considered.
Until !
is stabilized for this use, you can use an uninhabitable type like Infallible
(or any other empty enum) as a substitute for !
. Here is one way that you could do it:
type Enable = ();
type Disable = std::convert::Infallible;
enum Action<C, D> {
A,
B,
C(C),
D(D),
}
Now Action<Disable, Enable>
has the same properties as Action<!, ()>
would on nightly. Note the compiler is smart enough to figure out that the C
variant is uninhabitable even without usage of !
and so, like on nightly, match
does not require listing the uninhabitable variants.
(Once !
is stabilized you can keep the same source-compatible API by changing the Disable
alias to be !
instead of Infallible
.)