macro_rules! injective_enum_map {
{ $enum_ty:ty, $into:ty, $try_from:ty, $($body:tt)* } => { ... };
{ $enum_ty:ty, $into:ty, $try_from:ty } => { ... };
{ $enum_ty:ty, $both:ty, $($body:tt)* } => { ... };
{ $enum_ty:ty, $both:ty } => { ... };
}Expand description
Map an enum into and from another type (or two types) using From and TryFrom
(with unit error).
The enum type must be specified, followed by the type to map the enum into ($into),
optionally followed by the type to try to map into the enum ($try_from).
If $try_from is not specified, it is set to $into.
The two types must be similar enough that the same value expression (e.g., a numeric or string
literal) works for either; moreover, the expression must also be a valid pattern for a match
arm, so arbitrarily complicated expressions are not permitted.
In practice, the types should usually be the same, but specifying
two different types is useful for converting into &'static str and trying to convert
from &str, for example. Unintended options like mapping into Range<u8> and from u8
might be possible, but are not tested here. Note that non-unit variants are supported.
This map is intended to be “injective”; different enum variants should map into different
values, so that they can be mapped back unambiguously. The map may (or may not) also be
“surjective”, in which any possible value of the target type is associated with some enum
variant, in which case the TryFrom implementation would not be able to fail (but this macro
does not check for surjectivity). You should use bijective_enum_map if the mapping is
also surjective.
If the map is not injective, and multiple enum variants map to the same value, then a compiler
warning from #[warn(unreachable_patterns)] should be printed in most circumstances,
but it could be a silent logic error. In such a case, only the first enum variant
listed will be mapped from the duplicate value. Such a warning should also occur if an enum
variant is repeated.
§Examples
§Map into and from two other types:
use bijective_enum_map::injective_enum_map;
#[derive(Debug, PartialEq, Eq)]
enum AtMostTwo {
Zero,
One,
Two,
}
injective_enum_map! {
AtMostTwo, u8,
Zero <=> 0,
One <=> 1,
Two <=> 2,
}
injective_enum_map! {
AtMostTwo, &'static str, &str,
Zero <=> "zero",
One <=> "one",
Two <=> "two",
}
assert_eq!(u8::from(AtMostTwo::One), 1_u8);
assert_eq!(AtMostTwo::try_from(2_u8), Ok(AtMostTwo::Two));
assert_eq!(AtMostTwo::try_from(4_u8), Err(()));
// `<&str>::from` would also work
assert_eq!(<&'static str>::from(AtMostTwo::One), "one");
assert_eq!(AtMostTwo::try_from("two"), Ok(AtMostTwo::Two));
assert_eq!(AtMostTwo::try_from("four"), Err(()));§Empty enum, mapped into and from different types:
use bijective_enum_map::injective_enum_map;
#[derive(Debug, PartialEq, Eq)]
enum Empty {}
// The trailing comma is always optional
injective_enum_map! { Empty, &'static str, &str }
assert_eq!(Empty::try_from("42"), Err(()))§Intentionally violate injectivity:
use bijective_enum_map::injective_enum_map;
#[derive(Debug, PartialEq, Eq)]
enum Version {
V1,
V2Any,
V2,
V2Alternate,
V3,
}
// Slightly extreme option to silence warnings; you should probably run your code without
// a similar allow attribute in order to check that the warning which occurs is as expected.
// It seems that `#[allow(unreachable_patterns)]` is overridden by
// the `#[warn(unreachable_patterns)]` inside the macro.
#[allow(warnings)]
{
injective_enum_map! {
Version, u8,
// Because injectivity is violated, the order is significant.
// With this order, `2` is mapped to the `V2Any` variant.
V1 <=> 1,
V2Any <=> 2,
V2 <=> 2,
V2Alternate <=> 2,
V3 <=> 3,
}
}
assert_eq!(u8::from(Version::V2), 2);
assert_eq!(u8::from(Version::V3), 3);
assert_eq!(Version::try_from(1_u8), Ok(Version::V1));
assert_eq!(Version::try_from(2_u8), Ok(Version::V2Any));
assert_eq!(Version::try_from(5_u8), Err(()));§Map into and from another enum:
use bijective_enum_map::injective_enum_map;
#[derive(Debug, PartialEq, Eq)]
enum Enum {
One,
Two,
Three,
}
#[derive(Debug, PartialEq, Eq)]
enum Other {
Uno,
Dos,
Tres,
}
// Writing `Other` twice has the same effect as writing it once
injective_enum_map! {
Enum, Other, Other,
One <=> Other::Uno,
Two <=> Other::Dos,
Three <=> Other::Tres,
}
assert_eq!(Other::from(Enum::Three), Other::Tres);
// Note that this conversion cannot fail, but `injective_enum_map` does not know that.
// You could use `bijective_enum_map` instead.
assert_eq!(Enum::try_from(Other::Uno), Ok(Enum::One));