Destructuring a common field from many `enum` variants

I would like to know if there is a cleaner/less-verbose way to destructure a common field out of many enum variants.

For example, imagine I am working with the following enum.
Assume that the shape of this enum is determined by the format of external data I am deserializing (e.g. a JSON response from a REST API), and thus changing the shape of the data or using an alternative implementation (e.g. with traits) are not viable options:

Note: this is deliberately a toy example, as not to distract from the question at hand with the semantics of the actual application.
enum Pet {
    Cat {
        name: String,
        // Other fields omitted for brevity
    },
    Dog {
        name: String,
         // Other fields omitted for brevity
    },
    Fish {
        name: String,
         // Other fields omitted for brevity
    },
    Bird {
        name: String,
         // Other fields omitted for brevity
    },
    Hamster {
        name: String,
        // Other fields omitted for brevity
    },
}

I would like to extract the name field from a value of type Pet. Presently, the way I am achieving this is:

fn random_pet() -> Pet {
    // Implementation omitted
}

fn introduce_random_pet() {
    let name = match random_pet() {
        Pet::Cat { name } |
        Pet::Dog { name } |
        Pet::Fish { name } |
        Pet::Bird { name } |
        Pet::Hamster { name } => name
    };
    println!("The pet's name is {}", name);
}

This gets the job done, but is less than ideal because it's very verbose, and becomes even more verbose as more variants are added to the enum. Ideally Rust would provide a way to do this more ergonomically, something along the lines of the following pseudo-Rust:

fn introduce_random_pet() {
    let name = match random_pet() {
        _ { name } => name
    };
    println!("The pet's name is {}", name);
}

Does something like this exist in Rust, or is there any other way to express this concept in a way that is less verbose?

Changing the shape of the data might be viable options.

#[derive(Debug, serde::Deserialize)]
struct Pet {
    name: String,
    #[serde(flatten)]
    kind: Kind,
}

#[derive(Debug, serde::Deserialize)]
#[serde(tag = "kind")]
enum Kind {
    Cat { mew: String },
    Dog { bark: String },
    Fish { bubble: String },
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f41a2e36eb4e38585b370cd93b061eaa

https://serde.rs/field-attrs.html#flatten

2 Likes

This worked perfectly, thank you. I shouldn't have assumed so quickly that the data couldn't be reshaped.