Trust learning: reference and borrowing of ownership

Time:2021-3-16

When we use tuple to receive the return value of a function, we have to return the input parameter to the calling function.

A new reference method in trust

fn main() {
    let s1 = String::from("hello");

    let len = calculate_length(&s1);

    println!("The length of '{}' is {}.", s1, len);
}

fn calculate_length(s: &String) -> usize {
    s.len()
}

Compared with the method of using tuple, the above example:
1. All tuple codes in variable declaration and function return value disappear;
2. Pass & S1 to calculate_ Length, and in its definition, use & string instead of string.

This way(&Variable)Referred to as references, they allow us to refer to certain values without having to own them.
Note: the antonym of using & to reference is dereference, which is done by dereference operator *.

In the example above:

let s1 = String::from("hello");

let len = calculate_length(&s1);

&We can create a reference to S1, but not to S1. Because it is not owned, the value it points to will not be deleted when the reference is out of scope.
Similarly, the signature of the function uses & to indicate that the type of the parameter s is a reference. The explanation is as follows:

fn calculate_ Length (s: & string) - > use {// S is a reference to a string
    s.len()
}// here, the variable s is out of scope, but since it has no ownership (that is, the content it points to), nothing happens

The valid scope of variable s is the same as that of any function parameter, but when it exceeds the scope, because it has no ownership, trust will not delete the content pointed by the reference. When a function uses a reference as a parameter instead of an actual value as a parameter, trust does not need to return these values to return ownership, because it never owned ownership in this module.

In trust, reference is called function parameter borrowing. As in real life, if someone owns something, they can borrow it from them. When it’s done, we have to return it.

Can the borrowed content be modified? For example:

fn main() {
    let s = String::from("hello");

    change(&s);
}

fn change(some_string: &String) {
    some_string.push_str(", world");
}

$ cargo run
  |
7 | fn change(some_string: &String) {
  |                        ------- help: consider changing this to be a mutable reference: `&mut std::string::String`
8 |     some_string.push_str(", world");
  |     ^^^^^^^^^^^ `some_string` is a `&` reference, so the data it refers to cannot be borrowed as mutable

error: aborting due to previous error

For more information about this error, try `rustc --explain E0596`.
error: could not compile `ownership`.

To learn more, run the command again with --verbose.

Just as variables are immutable by default, so are references. Trust does not allow you to modify the content of the reference.

Variable reference
If I have to modify the value of a reference, let’s take a look at an example

fn main() {
    let mut s = String::from("hello");
    printfln!("{}",s);

    change(&mut s);
    printfln!("{}",s);
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}
//Running results
cargo run
    hello
    hello, world

In the above example: first, we have to change s to mut. Then we have to create a mutable reference using & mut s and use some_ String: & mut string takes a mutable reference.

But variable reference has a big limitation: it can only make a variable reference to a specific range of specific data.

For example, the following code will fail:

fn main() {
    let mut s = String::from("hello");

    let r1 = &mut s;
    let r2 = &mut s;

    println!("{}, {}", r1, r2);
}
$ cargo run
  |
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 here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0499`.
error: could not compile `ownership`.

To learn more, run the command again with --verbose.

Why are there such restrictions? This limitation enables rust to prevent data contention at compile time. Data contention is similar to contention condition and occurs when the following three behaviors occur:

*Two or more pointers access the same data at the same time.
*At least one pointer is used to write data.
*There is no mechanism for synchronous access to data.

But sometimes we want to have multiple mutable references, so we need to use scope – {}:

fn main() {
    let mut s = String::from("hello");

    {
        let r1 = &mut s;
    }// R1 is out of scope here, so we can create a new mutable reference without any problems

    let r2 = &mut s;
}

Reference to a variable usually has variable reference and immutable reference. If there are both variable reference and immutable reference, then there are still similar rules in trust

fn main() {
    let mut s = String::from("hello");

    Let R1 = & S; // no problem
    Let R2 = & S; // no problem
    Let R3 = & mut s; // problem

    println!("{}, {}, and {}", r1, r2, r3);
}
$ cargo run
  |
4 |     Let R1 = & S; // no problem
  |              -- immutable borrow occurs here
5 |     Let R2 = & S; // no problem
6 |     Let R3 = & mut s; // problem
  |              ^^^^^^ mutable borrow occurs here
7 | 
8 |     println!("{}, {}, and {}", r1, r2, r3);
  |                                -- immutable borrow later used here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0502`.
error: could not compile `ownership`.

To learn more, run the command again with --verbose.

When we have constant references, we cannot have variable references at the same time. The user of the invariant reference never wants the value of the reference to change suddenly from the bottom of the reference! However, you can use multiple immutable references, because no one has the ability to read only data and influence others to read it.

The scope of the reference starts where it was introduced and continues until the reference is used for the last time. For example:

fn main() {
    let mut s = String::from("hello");

    Let R1 = & S; // no problem
    Let R2 = & S; // no problem
    println!("{} and {}", r1, r2);
    //After that, R1 and R2 are no longer used, because they are out of scope, so they will not cause any trouble for subsequent references

    Let R3 = & mut s; // no problem
    println!("{}", r3);
}

Null pointer — hanging reference

In languages with pointers, it’s easy to create a dangling pointer by mistake, that is, by freeing some memory while retaining the pointer to the memory, to refer to a pointer that may have been allocated to a location in other people’s memory. In rust, by contrast, the compiler guarantees that references never hang: if we have references to some data, the compiler ensures that the data does not go beyond the reference to the data. (there is no concept of pointer in JS language)

For example:

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 // we return a reference to string -- s
}// here, s is out of range and discarded, and its storage disappears. It's very dangerous

$ cargo run

  |
5 | fn dangle() -> &String {
  |                ^ help: consider giving it a 'static lifetime: `&'static`
  |
  = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from

error: aborting due to previous error

For more information about this error, try `rustc --explain E0106`.
error: could not compile `ownership`.

To learn more, run the command again with --verbose.

Because s is created inside the Dangle, s will be released when the code of the dangle is finished. But we try to return a reference to it. This means that this reference will point to an invalid string. This caused the null pointer problem, and rust did not allow us to do so.

The solution is to return string directly:

fn main() {
    let string = no_dangle();
}

//This works. Ownership was removed and nothing was released.
fn no_dangle() -> String {
    let s = String::from("hello");

    s
}

Rules for reference:

*At any given time, you can have one variable reference or any number of immutable references.
*References must always be valid.

Recommended Today

Monkey patch monkey patch programming method and its application in Ruby

What ismonkey patch (Monkey Patch)? In a dynamic language, functions are added and changed without modifying the source code. Purpose of using monkey patch:1. Additional function2. Function change3. Fix program errors4. Add a hook to execute some other processing while executing a method, such as printing logs, realizing AOP, etc,5. Cache, when the amount of […]