Nick
Nick

Reputation: 6352

How to match a String against str in nested object?

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.

Upvotes: 1

Views: 140

Answers (1)

kmdreko
kmdreko

Reputation: 59817

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.

Upvotes: 2

Related Questions