包龙星

拿前朝的剑斩本朝的官?你这把剑的“有效期” (Lifetime) 早就过了!

方唐镜

大人英明!Rust 的引用必须保证在它活着的时候,它引用的数据也得活着,否则就是“悬垂引用”!

Rust 生命周期:保质期管理

生命周期 (Lifetimes) 是 Rust 引用有效性的作用域。大多数时候 Rust 能自动推断,但有时需要我们手动标注。

⏳ 借用检查器 (Borrow Checker)

Rust 编译器内建了一个借用检查器,用于比较作用域,确保所有的借用都是有效的。

fn main() {
    let r;
    {
        let x = 5;
        r = &x; // ❌ 错误!x 在这里就会死掉,但 r 还想在外面活
    }
    // println!("r: {}", r); 
}

🏷️ 生命周期标注语法

生命周期标注不会改变生命周期的长短,只是为了告诉编译器多个引用之间的关系。通常用 'a 表示。

// 这个函数返回的引用,其有效期等于 x 和 y 中较短的那个
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

🗿 静态生命周期 ('static)

'static 是一个特殊的生命周期,表示引用可以存活于整个程序的运行期间(例如字符串字面量)。

let s: &'static str = "我永远不死!";

🏠 结构体中的生命周期

如果结构体里包含引用,也需要标注生命周期。

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

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

动手时刻:比长短

练习生命周期标注。

  1. 定义一个函数 choose_one,接收两个字符串引用,返回其中一个。
  2. 为函数参数和返回值添加正确的生命周期标注 &'a
  3. 在 main 中创建两个不同作用域的字符串,调用该函数,看看编译器是否允许。
查看参考答案 (点击揭榜)

fn choose_one<'a>(x: &'a str, y: &'a str) -> &'a str {
    println!("我选第一个!");
    x
}

fn main() {
    let s1 = String::from("长命百岁");
    let result;
    {
        let s2 = String::from("短命鬼");
        // result = choose_one(&s1, &s2); 
        // ⚠️ 如果在这里解开注释,result 的生命周期就被限制为 s2 的生命周期了
        // 因为 'a 取的是较短的那个
    }
    // println!("Result: {}", result); // 这里会报错,因为 s2 已经死了
}
                    

成就解锁:【长生不老】 🐢