This is the 25th day of my participation in the November Gwen Challenge. Check out the event details: The last Gwen Challenge 2021
The variable reference
In the last article, we talked about the concept of borrowing, using the borrowing of a reference as a function parameter. Normally, we cannot modify the borrowed variable, but we can modify the borrowed variable by using a variable reference. A code example is as follows:
fn main() {
let mut s = String::from("hello"); // s is a mutable variable
change(&mut s); // &mut stands for mutable reference
}
fn change(some_string: &mut String) { // &mut stands for mutable reference
some_string.push_str(", world");
}
Copy the code
To modify borrowed variables, you must change s to MUt. Then you must create a mutable reference &mut s and accept a mutable reference some_string: &mut String.
But mutable references have one big limitation: there can only be one mutable reference for a particular data in a particular scope. For example, the following code will not compile successfully.
fn main() {
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
}
Copy the code
The compiler will throw the following exception:
error[E0499]: cannot borrow `s` as mutable more than once at a time --> src/main.rs:5:14 | 4 | let r1 = &mut s; | ------ first mutable borrow occurs here 5 | let r2 = &mut s; | ^^^^^^ second mutable borrow occurs here 6 | 7 | println! ("{}, {}", r1, r2); | -- first borrow later used hereCopy the code
So this modification of mutable references to borrowed variables allows modification in a restricted way, and the benefit of this restriction is that Rust can avoid data contention at compile time. A data race is similar to a race condition and can result from three behaviors:
- Two or more Pointers access the same data simultaneously.
- At least one pointer is used to write data.
- There is no mechanism for synchronous data access.
Data contention leads to undefined behavior that is difficult to track at run time, and difficult to diagnose and fix; Rust prevented that from happening. We can use {} to create a new scope that allows multiple mutable references, but not all in the same scope:
fn main() {
let mut s = String::from("hello");
{
let r1 = &mut s;
} R1 is out of scope here, so we can just create a new reference
let r2 = &mut s;
}
Copy the code
It is also important to note that you cannot have immutable references at the same time. Users of immutable references don’t want values to accidentally change right under their noses! But multiple immutable references are fine, because no one who can only read data has the ability to influence what other people read. Such as the following code:
fn main() {
let mut s = String::from("hello");
let r1 = &s; / / no problem
let r2 = &s; / / no problem
let r3 = &mut s; // Have mutable references while having immutable references
println!("{}, {}, and {}", r1, r2, r3);
}
Copy the code
The above code example will compile with the following exception:
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable --> src/main.rs:6:14 | 4 | let r1 = &s; // no problem | -- immutable borrow occurs here 5 | let r2 = &s; // no problem 6 | let r3 = &mut s; // BIG PROBLEM | ^^^^^^ mutable borrow occurs here 7 | 8 | println! ("{}, {}, and {}", r1, r2, r3); | -- immutable borrow later used hereCopy the code
But if mutable and immutable references do not overlap their scopes the code will compile, and we can modify the above code sample to work fine.
fn main() {
let mut s = String::from("hello");
let r1 = &s; / / no problem
let r2 = &s; / / no problem
println!("{} and {}", r1, r2);
// R1 and R2 are not used after this position
let r3 = &mut s; / / no problem
println!("{}", r3);
}
Copy the code
Dangling reference (Dangling References)
In a pointer language, it is very easy to incorrectly generate a dangling pointer by releasing memory while retaining the pointer to it. A dangling pointer refers to memory that has already been allocated to another holder. In Rust, by contrast, the compiler ensures that references never become dangling: When you have a reference to some data, the compiler ensures that the data does not go out of scope before it is referenced.
When we accidentally create a dangling reference, Rust throws an exception at compile time:
fn main() {
let reference_to_nothing = dangle();
}
fn dangle() - > &String { Dangle returns a reference to a string
let s = String::from("hello"); // s is a new string
&s Return a reference to the string s
}// where s leaves scope and is discarded. Its memory is freed.
Copy the code
Because s is created inside the Dangle function, s is released when dangle code completes execution. But let’s try to return a reference to it. This means that the reference will point to an invalid String, so at compile time Rust will throw an exception by simply returning String.
fn no_dangle() - >String {
let s = String::from("hello");
s
} // Ownership is moved out, memory is not freed
Copy the code
conclusion
The article was first published in the wechat public account Program Yuan Xiaozhuang, at the same time in nuggets.
The code word is not easy, reprint please explain the source, pass by the little friends of the lovely little finger point like and then go (╹▽╹)