[Rust Notes] Ownership Citation and Borrowing

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.

Tags: Back-end Rust programming language

Posted by merebel on Fri, 22 Jul 2022 23:20:45 +0530