Ownership 2 Reference and Borrowing
I am a big konjac. As a learner, I will record my understanding a little. If there are any mistakes, please advise!
1. Ownership and Function
Ownership moved to function parameter
Now, let's take a look at this program:
fn main() { let s = String::from("hello"); print_string(s); println!("{}", s); } fn print_string(s: String) { println!("{}", s); }
Compilation failed:
PS L:\Projects-Rust\rust-playground> cargo run Compiling rust-playground v0.1.0 (L:\Projects-Rust\rust-playground) error[E0382]: borrow of moved value: `s` --> src\main.rs:6:20 | 2 | let s = String::from("hello"); | - move occurs because `s` has type `String`, which does not implement the `Copy` trait 3 | 4 | print_string(s); | - value moved here 5 | 6 | println!("{}", s); | ^ value borrowed here after move | = note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info) For more information about this error, try `rustc --explain E0382`. error: could not compile `rust-playground` due to previous error
It will be found that it cannot be compiled, and the error hint given is the same as the compilation failure hint for **Example 2 ** in [Owner 1 Owner, Move, Clone] (. /Owner 1 Owner, Move, Clone.md), which is also borrow of moved value.
In fact, there is also a move in the process of passing s as an argument to printString(), and there is no move back.
This is not difficult to understand, because the parameter of the function is actually a variable in essence.
So what to do?
Hey, I'll just move it back again!
Return ownership by return value
Make the following modifications:
fn main() { let s = String::from("hello"); let s = print_string(s); println!("{}", s); } fn print_string(s: String) -> String { println!("{}", s); s }
output:
PS L:\Projects-Rust\rust-playground> cargo run Compiling rust-playground v0.1.0 (L:\Projects-Rust\rust-playground) Finished dev [unoptimized + debuginfo] target(s) in 0.67s Running `target\debug\rust-playground.exe` hello hello
run successfully.
However, it's a bit silly to write like this, is there a better way? The answer is yes, by using references.
2. Quoting and borrowing
A reference is actually like a pointer because it's actually an address, but it's different from a pointer because it's always guaranteed to point to a valid value.
When declaring a function parameter, you can add an amp ersand before the parameter type to indicate that the parameter is a reference type.
Correspondingly, an amp ersand is added before the parameter to create a corresponding reference:
fn main() { let s = String::from("hello"); print_string(&s); println!("{}", s); } fn print_string(s: &String) { println!("{}", s); }
output:
PS L:\Projects-Rust\rust-playground> cargo run Compiling rust-playground v0.1.0 (L:\Projects-Rust\rust-playground) Finished dev [unoptimized + debuginfo] target(s) in 0.70s Running `target\debug\rust-playground.exe` hello hello
Runs successfully without problems.
In addition, if you want to modify the original variable, declare the parameter and pass in the parameter, you need to write &mut, which is a variable reference.
But it should be noted that there can only be one mutable reference or multiple immutable references to a variable at the same time.
And what is **borrow**? It refers to the act of creating a reference.
3. Dangling references
See the program below:
fn main() { let reference_to_nothing = dangle(); } fn dangle() -> &String { let s = String::from("hello"); &s }
Compilation failed:
PS L:\Projects-Rust\rust-playground> cargo run Compiling rust-playground v0.1.0 (L:\Projects-Rust\rust-playground) error[E0106]: missing lifetime specifier --> src\main.rs:5:16 | 5 | fn dangle() -> &String { | ^ expected named lifetime parameter | = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from help: consider using the `'static` lifetime | 5 | fn dangle() -> &'static String { | ~~~~~~~~ For more information about this error, try `rustc --explain E0106`. error: could not compile `rust-playground` due to previous error
will find that it fails to compile, why?
Remember the important difference between references and pointers? It is always guaranteed to point to a valid value.
The key is in this sentence.
The owner s goes out of scope after the function ends and is destroyed, but returns a reference to it, which causes the reference to point to a piece of memory that may have been allocated to another owner. Well Rust certainly doesn't allow this to happen.