1

Let's say I have the following Rust enum:

enum Food {
    Fruit(String), // the String represents the food's name
    Vegitable(String),
}

How can I test that a function actually generates an apple and not a banana?

For instance, I want a test something like the following:

#[test]
fn is_correct_food() {
    let apple = Food::Fruit("Banana".to_string()); // comes from tested function
    assert!(matches!(apple, Food::Fruit("Apple".to_string())));
}

Playground link

When I run the above code, I get the following error:

error: expected one of `)`, `,`, `...`, `..=`, `..`, or `|`, found `.`
 --> src/lib.rs:8:48
  |
8 |     assert!(matches!(apple, Food::Fruit("Apple".to_string())));
  |                                                ^
  |                                                |
  |                                                expected one of `)`, `,`, `...`, `..=`, `..`, or `|`
  |                                                help: missing `,`

After doing some research, I learned this error occurs because you cannot call functions inside pattern matching statements. If I try and instead abstract the name into a variable (to remove the function call), the test passes because the pattern matches.

#[test]
fn is_correct_food() {
    let apple = Food::Fruit("Banana".to_string()); // comes from tested function
    let name = "Apple".to_string();
    assert!(matches!(apple, Food::Fruit(name)));
}

Playground link

Undesirable solution

It is possible to use a match statement to get the variable out from the contents. However, logic should be avoided in tests at all costs. Additionally, the actual object I need to test is a lot more complicated, which would require many nested match statements. So I'd like to avoid anything that looks like the following test:

#[test]
fn is_correct_food() {
    let apple = Food::Fruit("Banana".to_string()); // comes from tested function
    match apple {
        Food::Fruit(name) => assert_eq!(name, "apple"),
        _ => panic!("should be a fruit"),
    }
}

Related questions

How to match a String against string literals?

Does pattern matching with a top-level string. I want pattern matching inside a nested object.

Nick
  • 5,108
  • 2
  • 25
  • 58
  • Note that "abstracting the name into a variable" doesn't work: in the playground you linked the test should fail (because `"Apple" != "Banana"`) and yet it passes! Note also that the compiler warns you that the `name` variable is never used. This is because `Food::Fruit(name)` in a pattern creates a new variable called `name` instead of using the existing variable (btw: [playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=4cb31ea8641c58ee1b4e2e0f0b674279)). See also https://stackoverflow.com/q/28225958/5397009 – Jmb Dec 23 '22 at 06:28

1 Answers1

2

Much like you can add an if after the pattern in a match block, called a match guard, you can do the same with matches!:

matches!(apple, Food::Fruit(fruit) if fruit == "Apple")

The documentation for matches! mentions this functionality and includes an example that demonstrates it.

kmdreko
  • 42,554
  • 6
  • 57
  • 106