Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to combine tests for enum variant and value?

Tags:

In Rust, suppose I have an enum, and I'd like to do something conditional on its variant and some test involving its value. I'm having trouble finding a good way to write this.

For example, say I have an enum to represent farm animals, with the variant giving their species, and the value giving some other attributes such as their weight.

enum Animal {
    Horse(i32),
    Cow(i32, i32),
    Sheep,
    // and so on
}

I want a function to feed an animal. Most animals get hay, but a horse weighing under 700 kilos gets carrots instead.

Intuitively I want to write

fn feed(animal: Animal) {
    if let Animal::Horse(weight) = animal && weight < 700 { // ferris_is_confused.png
        // feed carrots
    } else {
        // feed hay
    }
}

but I get compilation errors "let expressions in this position are experimental" and "expected expression, found statement (let)".

I can instead write

fn feed(animal: Animal) {
    if let Animal::Horse(weight) = animal {
        if weight < 700 {
            // feed carrots
        } else {
            // feed hay
        }
    } else {
        // feed hay
    }
}

but then I have to duplicate the // feed hay code, which could be problematic if it is long or changes frequently.

I have the same problem if I use match: I can put a test if weight < 700 inside the Horse arm, but then I again have to include // feed hay in its else as well as in the _ arm.

Is there a standard idiomatic way to write this without duplicating code? This might be an FAQ but I may be searching for the wrong terms.

I'm using rustc 1.46.0.

like image 410
Nate Eldredge Avatar asked Sep 07 '20 17:09

Nate Eldredge


1 Answers

Match guards have been around for some time (Debian buster's 1.34.2 already has them) and support this use case in a less verbose fashion:

fn feed(animal: Animal) {
   match animal {
     Animal::Horse(weight) if weight < 700 => {
       // feed carrots
     }
     _ => {
       // feed hay
     }
   }
}

Alternatively, you can move the feed hay part into a local function and call that from multiple places.

like image 180
Florian Weimer Avatar answered Oct 02 '22 16:10

Florian Weimer