Variable assignment with pattern matching

We've seen many times how to assign a variable in Rust: we do something like let x = y;, which tells Rust to create a new variable named x and move or copy the value stored in y into it, as appropriate to the data type.

However, that's actually just a simplified case of what Rust is really doing, which is matching a pattern to a value and extracting data values from that matched pattern to store in the target variables, as in the following example:

pub struct DemoStruct {
pub id: u64,
pub name: String,
pub probability: f64,
}
// ...
let source1 = DemoStruct { id: 31, name: String::from("Example Thing"), probability: 0.42 };

let DemoStruct{ id: x, name: y, probability: z } = source1;

Okay, what just happened? First of all, we have a structure definition. We've seen those before, and the only new thing here is that we're using the String data type.

String has an interesting relationship with str. When we're using str, we almost always actually use a borrow such as &str or &'static str, rather than plain str. That's because plain str doesn't have a fixed size in the stack, which makes a lot of the things we'd want to do impossible to compile. So, we use &str instead, which does have a fixed size. But, using a reference as a contained value in a data structure also opens the door to all sorts of lifetime-based restrictions, so we don't really want to use pub name: &str here. Fortunately, we can use String instead.String can masquerade as an &str when we need it to, but it's not actually a borrow, so the ownership is straightforward. It is, however, slightly less efficient to use, so the general rule is to use String when it solves a problem and use &str the rest of the time.

After that, we create a new data value with the DemoStruct type, with its three contained values. We've seen that before, too.

What on Earth are we doing in the last line of the example? DemoStruct{ id: x, name: y, probability: z } is a pattern. We're telling Rust that we expect the assigned value to be a DemoStruct, and that its contained values should in turn be matched with the x, y, and z sub-patterns.

When we use a variable name as a pattern, it matches any value and that value is assigned to that name, which is what is happening here. It's also what's happening with a simple let x = 5. So, x, y, and z end up being new variables containing the values that were previously stored in source.id, source.name, and source.probability, respectively.

We didn't have to use variable names for the sub-patterns, though. We could, for example, have tried this:

DemoStruct{ id: 31, name: y, probability: z } = source1;

If we do that, however, the compiler will report an error. The error is not because 31 is an invalid pattern. It's a perfectly good pattern and even happens to match the value we would actually find. The compiler will refuse to compile it, though, because it doesn't match all of the possibilities for the source value, and Rust doesn't allow let statements that might fail just because a variable's value got changed. Imagine of all the trouble that it could cause!

The Rust compiler refers to being able to handle all of the possibilities when pattern matching as covering.

For patterns that might or might not match the input value, we can use if let expressions instead.

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

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