说生命周期,最经典的莫属空指针问题了:

fn main() {
    {
        let r;

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

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

可以看到x的作用域在打印之前就已经不起作用了,但是r对其进行了一个引用,所以在打印出仍旧需要使用x,因此就造成了空指针问题。解决办法就是转移将x的所属权,比如:r = x;

吐槽一下所有权,所有权并不是说变量被所有了,而是变量所指向的值被变量单独所拥有,被别的变量借走了就变成了别的了,就不再拥有所有权了。

来看一个实例,比如我们比较两个字符串那个长度更长久返回那个,我们可能这样编写程序:

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
    }
}

看似没有问题,但是当我们运行的时候会发现:

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.

程序报错了。错误信息居然是编译的时候检查到返回的值要有所有权也就是要返回一个在生命周期中的值的变量。就像之前的空指针问题一样,所以我们需要在返回和入参的时候考虑到是否可以把生命周期也就是所有权也传入和返回呢,当然可以,错误提示中已经给到了解决方法。

先来看一下rust中含有生命周期引用的方法(' + 一个小写字母):

&i32        // 普通引用
&'a i32     // 含有生命周期的引用
&'a mut i32 // 可变形含有生命周期的引用

如此我们就可以很轻松地改造上面的例子:

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

运行结果为:

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

但是不管怎样,生命周期毕竟是比较有意思的,比如我们将main函数改写一下:

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);
}

那么运行结果就变成了:

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.

与之相应的一个变例(其实都跟最开始的小示例类似):

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.

以上说明了一个事实,如果含有生命周期引用和返回的变量值只在跟起相关的所有变量都有效果的情况下才有效否则失效.

结构体中使用字符串切片引用
之前所有的例子中返回的都是String并没有字符串切片&str类型的,现在来看其实已经很简单因为&str只是String的一个部分引用,所以当String失效的时候其必然失去了生命周期,因此出错,那么现在就可以来解决该问题了:

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

使用泛型情形

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 {

方法定义中的生命周期注释

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,
    };
}

要求在impl之后声明生命周期参数,并在类型名称后使用它,但是由于第一个省略规则,我们不需要注释对self的引用的生命周期。

这是第三个生命周期删除规则适用的示例:

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

静态生命周期:

生命周期注释有一个特别的:'static 。所有用双引号包括的字符串常量所代表的精确数据类型都是 &'static str'static 所表示的生命周期从程序运行开始到程序运行结束。

例如:let s: &'static str = "我是一个静态生命周期";

泛型,特征,生命周期在一起:

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
    }
}

运行结果:

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

子康
10 声望0 粉丝