Trust learning life cycle

Time:2021-2-23

When it comes to life cycle, the most classic problem is null pointer

fn main() {
    {
        let r;

        {
            let x = 5;
            r = &x;
        }

        println!("r: {}", r);
    }
}

It can be seen that the scope of X does not work before printing, but r makes a reference to it, so x still needs to be used before printing, which causes the problem of null pointer. The solution is to transfer the ownership of X, such as:r = x;

Make complaints about ownership. Ownership does not mean that variables are all, but that the value of variables is owned by variables. It is changed by borrowing other variables and no longer ownership.

Let’s take a look at an example. For example, if we compare two strings, the length of which is longer, and return which, we may write a program like this:

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

It seems that there is no problem, but when we run it, we will find that:

D:\learn\cargo_learn>cargo run
   Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
error[E0106]: missing lifetime specifier
 --> src\main.rs:9:33
  |
9 | fn longest(x: &str, y: &str) -> &str {
  |               ----     ----     ^ expected named lifetime parameter
  |
  = help: this function's return type contains a borrowed value, but the signature does not say whether it is borrowed from `x` or `y`
help: consider introducing a named lifetime parameter
  |
9 | fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  |           ^^^^    ^^^^^^^     ^^^^^^^     ^^^

error: aborting due to previous error

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

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

The program reported an error. The error message is that the returned value should have ownership when compiling, that is, the variable that returns a value in the life cycle. Just like the previous null pointer problem, we need to consider whether we can pass in and return the life cycle, that is, ownership, when returning and entering parameters. Of course, the solution has been given in the error prompt.

Let’s first take a look at the methods in trust that contain lifecycle references (‘+ a lowercase letter)

&I32 // normal reference
&'a I32 // reference with lifecycle
&'a mut I32 // deformable reference with lifecycle

In this way, we can easily transform the above example:

fn longest<'s>(x: &'s str, y: &'s str) -> &'s str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

The running results are as follows

D:\learn\cargo_learn>cargo run
   Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
    Finished dev [unoptimized + debuginfo] target(s) in 0.72s
     Running `target\debug\cargo_learn.exe`
The longest string is abcd

However, life cycle is more interesting after all. For example, we rewrite the main function:

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
    }
    println!("The longest string is {}", result);
}

Then the running result becomes:

D:\learn\cargo_learn>cargo run
   Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
error[E0597]: `string2` does not live long enough
 --> src\main.rs:6:44
  |
6 |         result = longest(string1.as_str(), string2.as_str());
  |                                            ^^^^^^^ borrowed value does not live long enough
7 |     }
  |     - `string2` dropped here while still borrowed
8 |     println!("The longest string is {}", result);
  |                                          ------ borrow later used here

error: aborting due to previous error

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

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

A corresponding variant (in fact, it’s all similar to the first small example) is as follows:

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest(string1.as_str(), string2);
    println!("The longest string is {}", result);
}

fn longest<'a>(x: &str, y: &str) -> &'a str {
    let result = String::from("really long string");
    result.as_str()
}
D:\learn\cargo_learn>cargo run
   Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
warning: unused variable: `x`
 --> src\main.rs:9:16
  |
9 | fn longest<'a>(x: &str, y: &str) -> &'a str {
  |                ^ help: if this is intentional, prefix it with an underscore: `_x`
  |
  = note: `#[warn(unused_variables)]` on by default

warning: unused variable: `y`
 --> src\main.rs:9:25
  |
9 | fn longest<'a>(x: &str, y: &str) -> &'a str {
  |                         ^ help: if this is intentional, prefix it with an underscore: `_y`

error[E0515]: cannot return value referencing local variable `result`
  --> src\main.rs:11:5
   |
11 |     result.as_str()
   |     ------^^^^^^^^^
   |     |
   |     returns a value referencing data owned by the current function
   |     `result` is borrowed here

error: aborting due to previous error; 2 warnings emitted

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

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

The above shows the fact that if the values of variables with life cycle references and returns are valid only when all related variables have effects, otherwise they will be invalid

Using string slice references in structs
In all the previous examples, string is returned without string slice & STR type. Now it is very simple, because & STR is only a partial reference of string, so when string fails, it will lose its life cycle, so there is an error. Now we can solve the problem

fn main() {
    struct Str<'a> {
        content: &'a str
    }
    let s = Str {
        content: "string_slice"
    };
    println!("s.content = {}", s.content);
}

D:\learn\cargo_learn>cargo run
   Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
    Finished dev [unoptimized + debuginfo] target(s) in 0.53s
     Running `target\debug\cargo_learn.exe`
s.content = string_slice

Using generics

fn first_word<'a>(s: &'a str) -> &'a str {

fn foo<'a, 'b>(x: &'a i32, y: &'b i32) -> &'c i32 {

fn first_word(s: &str) -> &str {

fn first_word<'a>(s: &'a str) -> &str {

fn first_word<'a>(s: &'a str) -> &'a str {

Lifecycle annotation in method definition

struct ImportantExcerpt<'a> {
    part: &'a str,
}

impl<'a> ImportantExcerpt<'a> {
    fn level(&self) -> i32 {
        3
    }
}

impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let i = ImportantExcerpt {
        part: first_sentence,
    };
}

It is required to declare the lifecycle parameter after impl and use it after the type name, but due to the first omission rule, we do not need to annotate the lifecycle of the reference to self.

This is an example of how the third lifecycle deletion rule applies:

impl<'a> ImportantExcerpt<'a> {
    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

Static life cycle:

Life cycle annotation has a special feature:'static . All string constants enclosed in double quotation marks represent the exact data type &'static str'staticThe life cycle represented by is from the beginning of program running to the end of program running.

For example:Let s: & 'static STR ='I am a static life cycle';

Generics, features, lifecycles together:

fn main() {
    let string1 = String::from("abcd");
    let string2 = "xyz";

    let result = longest_with_an_announcement(
        string1.as_str(),
        string2,
        "Today is someone's birthday!",
    );
    println!("The longest string is {}", result);
}

use std::fmt::Display;

fn longest_with_an_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str
    where
        T: Display,
{
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

Results of operation:

D:\learn\cargo_learn>cargo run
   Compiling cargo_learn v0.1.0 (D:\learn\cargo_learn)
    Finished dev [unoptimized + debuginfo] target(s) in 0.65s
     Running `target\debug\cargo_learn.exe`
Announcement! Today is someone's birthday!
The longest string is abcd