Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unwrap inner type when enum variant is known

Tags:

enums

rust

I have this enum type:

enum Animal {     Dog(i32),     Cat(u8), } 

Now I have a function that takes this type as parameter. I know (for some reason) that the input is always a Cat. I want to achieve this:

fn count_legs_of_cat(animal: Animal) -> u8 {     if let Animal::Cat(c) = animal { c } else { unreachable!() } } 

Can I write this shorter and/or more idiomatic?

like image 949
Lukas Kalbertodt Avatar asked Jan 22 '16 18:01

Lukas Kalbertodt


1 Answers

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,         }     } } 
like image 118
Shepmaster Avatar answered Oct 16 '22 07:10

Shepmaster