Moving and borrowing in pattern matches

When we match a pattern that contains variables, the matching data values are moved into the variables (unless their data type has the Copy trait). For example, this will cause the compiler to report an error, even though at first glance it seems reasonable, especially for people who are used to other programming languages:

let source5 = DemoStruct { id: 40, name: String::from("A Surprising Thing"), probability: 0.93 };

if let DemoStruct {id: 41, name: x, probability: _} = source5 {
println!("Extracted name: {}", x);
}

println!("source5.name is {}", source5.name);

The problem is that, after the if let, source5.name does not (or at least might not) contain a value anymore, because that value was moved to the x variable. The compiler can't be sure that the final println! command will always be valid, which is a problem because it happens whether the if let block gets run or not.

Can we borrow the value in if let, instead of moving it? That way, future uses of the value would still be valid. The answer is yes, but there's a problem we need to get a handle on. We can try this:

let source5 = DemoStruct { id: 40, name: String::from("A Surprising Thing"), probability: 0.93 };

if let DemoStruct {id: 41, name: &x, probability: _} = source5 {
println!("Extracted name: {}", x);
}

println!("source5.name is {}", source5.name);

But what we find is that the compiler complains that it expected String in the pattern, and found a reference instead. That's because using an & in the pattern this way doesn't mean we want to borrow the value; it means we expect the value to already be a borrow in the source data.

To tell Rust that we want to borrow a value that is matched by a variable in a pattern, we need to use a new keyword: ref. That looks like this:

let source5 = DemoStruct { id: 40, name: String::from("A Surprising Thing"), probability: 0.93 };

if let DemoStruct {id: 41, name: ref x, probability: _} = source5 {
println!("Extracted name: {}", x);
}

println!("source5.name is {}", source5.name);

At last, the compiler is happy, and so are we. The value in source5.name is not a borrow, but when we match source5 to our pattern, we borrow it into the x variable, which means it is not moved out of source5, and the final println! will always work.

The ref keyword and the & symbol are closely related. These two lines of code do exactly the same thing:

let ref borrowed1 = source5;
let borrowed2 = &source5;

The difference between them is syntactic: we apply the ref keyword to the variable where the borrow will be stored, while we apply the & symbol to the variable where the value to be borrowed is already stored. We choose which one to use based on which end of the borrow we're writing, and we don't need both.

In fact, using both creates a borrow of a borrow of the original value, which is not usually what we want. The Rust compiler can automatically dereference any number of layers of borrowing in order to find the value it needs for a function parameter, so something like this works fine without causing any errors:

pub fn borrow_demostruct(x: &DemoStruct) {
println!("Borrowed {}", x.name);
}
let ref borrowed_borrow = &source5;
borrow_demostruct(borrowed_borrow);

The compiler sees that the borrow_demostruct function wants a borrow of DemoStruct, and that the value we're trying to send it is a borrow of a borrow of a DemoStruct, so it dereferences that value once and passes that to the function parameter. Everything works.

What does a borrow of a borrow means? Well, first of all, we had a DemoStruct value. Then, we borrowed it, giving us an &DemoStruct value. Then, we borrowed that value, giving us an &&DemoStruct value.

However, the computer had to put in a little more effort than was necessary to achieve the same result. Multiple levels of borrowing should only be used when they solve a problem, because using them when we don't need them is just wasteful.

Also, &&DemoStruct is not actually the same data type as &DemoStruct, despite the fact that the Rust compiler can figure out how to treat the former as the latter when it's used as a function parameter. Sometimes, that matters.

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

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