Quotes and borrowing
If ownership transfer occurs every time, the writing of the program will become extremely complicated. Therefore, rust is similar to other programming languages and provides reference methods to operate.Get a reference to a variable, called a borrow. It's similar to what you borrow from someone else to use, but the owner of this thing is not you.No transfer of ownership occurs in reference。
Use of citations
In rust, the syntax of reference is very simple. Take the reference by &, and dereference by *. For example:
fn main() { let s1: String = "Hello".to_string(); let s2: &String = &s1; // s2 refers to s1 println!("{s1}"); println!("{s2}"); }
This code can run normally because s1 referenced by s2 will not transfer ownership. Let’s take a look at another example, passing function parameters by reference.
fn main() { let s = "Hello".to_string(); let len = calculate_length(&s); // Quote println!("{s}"); println!("{len}"); } fn calculate_length(s: &String) -> usize { () }
In calculate_length, s is a reference, which does not have ownership. Therefore, when the function call ends, although the scope of s is ended, drop will not be called.
Variable versus immutable citations
In the example just now, we just got the length of the string, which is equivalent to reading the variable. In rust, references are also immutable by default. If you need to change the variable through reference modification, then a variable reference must be used. Variable references, like mutable variables, are implemented through the keyword mut. For example:
fn main() { let mut s = String::from("hello"); change(&mut s); // Variable references println!("{s}"); } fn change(some_string: &mut String) { some_string.push_str(", world"); }
This code outputshello, world
, it can be seen that we modified the value of s through variable references, but there is no transfer of ownership involved in this process.
In fact, things are not that simple. Variable references are not allowed to be used as you wish. It has a big limitation,"One variable can only have one variable reference in the same scope". For example:
fn main() { let mut s = String::from("hello"); let r1 = &mut s; let r2 = &mut s; // Two variable references cannot be created in the same scope. println!("{}, {}", r1, r2); }
Two variable references may cause "written simultaneously", resulting in memory insecure situations. If in different scopes, there can be multiple variable references, but they cannot be owned at the same time. For example:
fn main() { let mut s = String::from("hello"); { let r1 = &mut s; println!("{r1}"); } // r1 is out of scope here, so we can completely create a new reference let r2 = &mut s; println!("{r2}"); }
At the same time, rust does not allow mutable and immutable references to exist at the same time. Because immutable references may become invalid due to variable references. The following is a C++ code to illustrate this point.
#include<vector> #include<string> #include<iostream> using namespace std; int main() { // Readable reference becomes invalid due to variable reference vector<string> vs; vs.push_back("hello"); auto & elem = vs[0]; vs.push_back("world"); // push_back will cause the entire memory pointed to by vs to be re-allocated and moved to another address, and all the references in the iterator will become dangled pointers. cout << vs[0] << endl; cout << elem << endl; // Try to use a dangled pointer return 0; }
After this code is executed, the result is as follows:
hello
Segmentation fault (core dumped)
Obviously, the segfault here is caused precisely by attempts to use a dangled pointer. The special variable reference and immutable reference mechanism of rust avoid this error. For example:
fn main() { let reference_to_nothing = dangle(); } fn dangle() -> &String { let s = String::from("hello"); &s } // returnsQuotation,Function end,sMove out of scope,CalldropFunctions clean memory,那么returnQuotation将会变成悬垂引用,This raises an error。
This rust code cannot be compiled and passed, thus avoiding runtime errors like the C++ code above.
As stated in Rust Programming Language
This limitation allows variability in a very careful way, preventing multiple mutable references to the same data at the same time. New Rustaceans often struggle to adapt to this, because variables in most languages are variable at any time. The advantage of this limitation is that Rust can avoid data competition at compile time. Data race is similar to race conditions, and it can be caused by these 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 synchronizing data access.
Rust's compiler has been optimizing. In the early days, the scope of references was consistent with the scope of variables, which caused great trouble for daily use. You must be very careful to arrange the borrowing of mutable and immutable variables to avoid being unable to be compiled, such as the following code:
fn main() { let mut s = String::from("hello"); let r1 = &s; let r2 = &s; println!("{} and {}", r1, r2); // In the new compiler, the r1 and r2 scope end here let r3 = &mut s; println!("{}", r3); } // In the old compiler, the r1, r2, and r3 scopes end here // In the new compiler,r3The scope ends here
In the old version of the compiler (before Rust 1.31), an error will be reported because the scope of r1 and r2 ends at curly braces }, and the borrowing of r3 will trigger the inability to borrow both mutable and immutable rules. But in the new compiler, the code will pass smoothly because the end position of the reference scope changes from curly braces to the last used position, so r1 borrows and r2 borrows in println!, and r3 can borrow smoothly to the variable reference.
NLL
For this compiler optimization behavior, Rust specifically named Non-Lexical Lifetimes(NLL), which is specifically used to find the code location where a reference is no longer used before the end of scope (}).
Summarize
- In general, the borrowing rules are as follows:
- At the same time, you can only have either one variable reference or any multiple immutable references that must always be valid.
Rust Programming Language
Why can there be only one variable reference in Rust in a single thread?
Rust Language Bible
This is the end of this article about rust quotations and borrowing. For more related rust quotations and borrowing content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!