Accessing borrowed data

To receive borrowed data, we need to properly specify the data type as a borrow. That is done by using & or &mut with the data type on the receiving end, just as we used them with the data value on the sending end.

While the term borrow is common in Rust, the technical term is reference. So, we will usually say we are borrowing data, using borrowed data, or that a data value is accessed as a borrow, but we could also say that we are referencing data, using referenced data, or that a data value is accessed by reference.

Here, we have the definition of two functions, the same two functions we used in our previous examples. Look at the data types specified for the point parameter on each one:

pub fn borrow_ownership(point: &Point2D) {
println!("Point2D{{x: {}, y: {}}} is now borrowed by a new scope", point.x, point.y);
}

pub fn borrow_ownership_mutably(point: &mut Point2D) {
println!("Point2D{{x: {}, y: {}}} is now borrowed by a new scope", point.x, point.y);
point.x = 13.5;
println!("Borrowed value changed to Point2D{{x: {}, y: {}}}", point.x, point.y);
}

Being a borrow or mutable borrow is part of the data type for the parameter. That means that the compiler knows the value passed to the parameter must be a borrow and will refuse to compile code that tries to pass a non-borrowed value to the function.

Most of the time, using a borrowed value is the same as using a non-borrowed value, as we can see in these functions. They interact with point just as if it were a locally owned variable.

However, that's because the compiler is smart. The truth is that a borrow is a memory address for a data value, not the data value itself (which is the bytes stored in memory at that address). Most of the time, the compiler can figure out that it needs to take the extra step of looking in the local variable for the address, then looking in memory at that address for the data, rather than just looking in the local variable for the data.

This process is called dereferencing. For some reason, nobody ever says deborrowing.

There are times when the compiler can't figure out that we want to dereference and handle it automatically. In those situations, we can use the * symbol to manually dereference a borrowed value.

The most common place where this comes up is in assigning to a borrowed value. If the borrowed value is a structure or something that has internal data, we can assign to the internal data with no problem, but when we want to assign a whole new value to a borrowed variable, we need to use dereferencing.

This code tries to assign the value as if it weren't a borrow:

pub fn set_to_six(value: &mut u32) {
value = 6;
}

We see that value is a mutable borrow of a 32-bit unsigned integer. When we try to assign to that variable directly, the compiler tells us this:

What's happening here is that there's no way for the compiler to tell the difference between I want to assign this value to be what is stored in memory at the referenced location and I want this reference variable to refer to a different memory location. It needs to assume one of those and let us tell it if we want the other, and the assumption it picks is the second one.

It makes sense to pick assign a new value to this variable as the default, since that's what = means in any other situation as well. Working with borrowed data is a special case, not the default.

The advice given by the compiler here is also based on the assumption that what we want to do is have value refer to a new memory address, which means that if we were to follow it blindly, the compiler error would go away, but the program would not do what we want. Instead of storing the number 6 in the borrowed variable, it would set the value variable to contain a new borrow.

What we actually want to do is this:

pub fn set_to_six(value: &mut u32) {
*value = 6;
}

That tells the compiler that instead of assigning to value, we want to assign through value to the originally borrowed variable.

The * symbol can be used for both reading and writing the borrowed value, and can be used even when not strictly required if we want to be explicit.

Even though dereferencing and multiplication are written using the same symbol, the compiler never gets them confused. Multiplication is not a valid operation on a borrow, and dereferencing isn't a valid operation on a number. Additionally, multiplication always needs a data value on both sides of the *, while dereferencing always needs a data value on only one side. Between those two pieces of information, the compiler has more than enough to know which operation we're asking for.
..................Content has been hidden....................

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