Using if let expressions to test whether a pattern matches

Using pattern matching to unpack values into multiple variables can be useful, but using pattern matching to make decisions is where this functionality really shines, as in the following example:

let source2 = DemoStruct { id: 35, name: String::from("Another Thing"), probability: 0.42 };
let source3 = DemoStruct { id: 63, name: String::from("Super Thing"), probability: 0.99 };

if let DemoStruct { id: 63, name: y, probability: z } = source2 {
println!("When id is 63, name is {} and probability is {}", y, z);
}

if let DemoStruct { id: 63, name: y, probability: z } = source3 {
println!("When id is 63, name is {} and probability is {}", y, z);
}

Here, we've defined two more variables containing DemoStruct values, and then used pattern matching to pull them back apart and assign their contained values to individual variables. This time, though, we did it in an if let expression instead of a let expression. That makes a world of difference, because now the pattern doesn't have to cover the whole domain of possible input values. If the pattern matches, the if let expression runs the code in its block. If the pattern doesn't match, then the if let expression doesn't run the code. It's conditional.

Since the pattern doesn't have to cover the domain, it means we can use 63 as a pattern to match against the id value, and there's nothing wrong with that. The same principle applies to more complex patterns or any pattern that only matches a subset of the values that might be represented by the data type it's matched against.

We can combine if let with normal if and else expressions, to create more complex decision-making structures, in a couple of ways.

First, we can put an if let expression inside the block of an if, else if, or else expression, and vice versa. That comes naturally, since there's nothing unusual about those block expressions—no special restrictions are placed on them just because they're inside a conditional expression.

Second, we can combine if let or else if let into the same chain of options as if, else if, and else. That looks like this:

    if false {
println!("This never happens");
}
else if let DemoStruct{ id: 35, name: y, probability: z } = source4 {
println!("When id is 35, name is {} and probability is {}", y, z);
}
else if let DemoStruct{ id: 36, name: y, probability: z } = source4 {
println!("When id is 36, name is {} and probability is {}", y, z);
}
else {
println!("None of the conditions matched");
}

The chain has to start with an if or if let expression (whichever one we need), and can then have any number of else if or else if let, and finally an else if expression we need one.

We're still just pulling data values out of our structures with the pattern matching, though. We can do more when the pattern is matched against other kinds of data type. An important one is the Result data type we've talked about previously. Remember that Result can be either Ok or Err, and either way it contains a value, either a result value or an error value of some sort. We saw before how to use the ? or assorted functions to deal with Result, but we can also handle it with pattern matching, and that's often going to be the way we'll choose.

So, here's a function that constructs a DemoStruct value for us, but it only does it if the id value we ask for is even (the remainder when divided by two is zero). This function gives us a Result containing the created DemoStruct value or an error message:

pub fn might_fail(id: u64) -> Result<DemoStruct, &'static str> {
if id % 2 == 0 {
Ok(DemoStruct { id: id, name: String::from("An Even Thing"), probability: 0.2})
}
else {
Err("Only even numbers are allowed")
}
}

If we then call that function, we can use pattern matching to figure out if it succeeded or failed:

    if let Ok(x) = might_fail(37) {
println!("Odd succeeded, name is {}", x.name);
}

if let Ok(x) = might_fail(38) {
println!("Even succeeded, name is {}", x.name);
}

Here, we're calling might_fail twice, once with an odd number as the parameter value, and once with an even one. Both times, we use pattern matching to check whether the result is Ok and assign the contained value to a variable called x if it is.

Ok is not a data structure and neither is Err. We'll learn more about what they are in the next chapter. The point for now is that pattern matching gives us a simple way to check whether Result represents a success or a failure, and to handle one or both cases easily.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.12.108.175