Unwrap inner type when enum variant is known
Not really. What I have seen is introducing a new struct
for each enum variant, and then methods on the enum to decompose it:
struct Dog(i32);
struct Cat(u8);
enum Animal {
Dog(Dog),
Cat(Cat),
}
impl Animal {
fn cat(self) -> Cat {
if let Animal::Cat(c) = self { c } else { panic!("Not a cat") }
}
fn dog(self) -> Dog {
if let Animal::Dog(d) = self { d } else { panic!("Not a dog") }
}
}
// Or better an impl on `Cat` ?
fn count_legs_of_cat(c: Cat) -> u8 {
c.0
}
Of course, you don't need the struct and you could just return the u8
, but that may get hard to track.
There's a glimmer of better support for this in the future, however. I think it's the "efficient code reuse" RFC, but better described in the blog post Virtual Structs Part 3: Bringing Enums and Structs Together. The proposal would be to allow Animal::Cat
to be a standalone type, thus your method could accept an Animal::Cat
and not have to worry about it.
Personally, I almost always prefer to write the infallible code in my inherent implementation and force the caller to panic:
impl Animal {
fn cat(self) -> Option<Cat> {
if let Animal::Cat(c) = self {
Some(c)
} else {
None
}
}
fn dog(self) -> Option<Dog> {
if let Animal::Dog(d) = self {
Some(d)
} else {
None
}
}
}
And I'd probably use a match
impl Animal {
fn cat(self) -> Option<Cat> {
match self {
Animal::Cat(c) => Some(c),
_ => None,
}
}
fn dog(self) -> Option<Dog> {
match self {
Animal::Dog(d) => Some(d),
_ => None,
}
}
}
Try enum-as-inner crate, it work exactly what Shepmaster's answer have done.
I found one single macro is the best way to solve the problem (in recent Rust).
Macro Definition
macro_rules! cast {
($target: expr, $pat: path) => {
{
if let $pat(a) = $target { // #1
a
} else {
panic!(
"mismatch variant when cast to {}",
stringify!($pat)); // #2
}
}
};
}
Macro Usage
let cat = cast!(animal, Animal::Cat);
Explanation:
-
#1 The if let exploits recent Rust compiler's smart pattern matching. Contrary to other solutions like
into_variant
and friends, this one macro covers all ownership usage likeself
,&self
and&mut self
. On the other hand{into,as,as_mut}_{variant}
solution usually needs 3 * N method definitions where N is the number of variants. -
#2 If the variant and value mismatch, the macro will simply panic and report the expected pattern.
-
The macro, however, does not handle nested pattern like
Some(Animal(cat))
. But it is good enough for common usage.
I wrote a small macro for extracting the known enum variant:
#[macro_export]
macro_rules! extract_enum_value {
($value:expr, $pattern:pat => $extracted_value:expr) => {
match $value {
$pattern => $extracted_value,
_ => panic!("Pattern doesn't match!"),
}
};
}
let cat = extract_enum_value!(animal, Animal::Cat(c) => c);
However, I'm not sure if this fits into your need.