Welcome toVigges Developer Community-Open, Learning,Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
1.7k views
in Technique[技术] by (71.8m points)

rust - Unwrap inner type when enum variant is known

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?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Answer

0 votes
by (71.8m points)

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,
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome to Vigges Developer Community for programmer and developer-Open, Learning and Share
...