回顾所有权:引用到底能活多久

上一篇我们聊过 Rust 的所有权规则:一个值同一时刻只能有一个所有者,离开作用域就被 drop。但实际写代码时,我们经常把"引用"到处传来传去——这就引出一个问题:这个引用,会不会在它指向的值被释放之后,还被人用到?

Rust 的解决办法是:在编译期就把每个引用的"活多久"算清楚。大多数时候编译器能自己推断,但有些情况它需要你写出来——这就是生命周期标注。

生命周期不是运行时的东西,它只是给编译器看的一段注释,用来检查"这个引用一定比那个引用活得久"。
本文核心知识点
  • 'a生命周期标注的写法,本质是给引用起个名字。
  • 省略规则大多数普通函数其实不用写标注。
  • trait定义一组方法签名,让不同类型共享行为。
  • trait bound用泛型时声明"这个类型必须实现某个 trait"。
Rust 生命周期与 trait 配图:深蓝渐变背景,中央一个发光的盾牌形核心,周围环绕多层同心发光圆环,圆环上挂载多个发光小立方体节点,节点之间用发光的借用连线相连,部分连线带箭头
中央盾牌是所有权,外围同心圆环是作用域,节点之间的连线就是带生命周期的借用。

生命周期标注:给引用加个名字

下面这段代码编译器会报错:返回的到底是 x 还是 y?它不知道返回的引用该活多久。

// 编译报错:返回的引用没有标注生命周期
fn longest(x: &str, y: &str) -> &str {
    if x.len() > y.len() { x } else { y }
}

加上生命周期标注 'a 就清楚了:返回的引用活得和 x、y 中较短的那个一样长。这个 'a 是你给这几个引用起的共同名字。

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

注意标注不改变任何运行时行为,只是告诉编译器"它们的生存期是关联的",让借用检查器能放心放行。

函数里的生命周期:三个常见写法

好消息是 Rust 有"生命周期省略规则",三种常见情况其实不用写:

  • 每个引用参数自动分到一个独立生命周期;
  • 如果只有一个输入引用,它的生命周期会赋给所有输出引用;
  • 如果是方法(self 是引用),self 的生命周期赋给输出。

所以 fn first_word(s: &str) -> &str 完全合法,编译器自己推。只有当输入有多个引用、输出又和它们都有关系时,才需要你手写 'a。

结构体里持有引用

如果结构体里想存一个引用(比如借用一段字符串而不是复制它),结构体也得标注生命周期,确保"结构体活多久,它借用的数据就得活多久"。

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

fn main() {
    let novel = String::from("从前有座山。山上有座庙。");
    let ex = Excerpt {
        part: novel.split('.').next().unwrap(),
    };
    println!("{}", ex.part);  // 从前有座山
}

只要 novel 在 ex 之前不被 drop,这段就安全——借用检查器替你盯着。

trait:给类型定义共同行为

trait 类似其他语言里的"接口":它定义一组方法签名,多个类型可以去实现它。下面定义一个 Summarizable trait,让 NewsArticle 和 Tweet 都实现它。

pub trait Summarizable {
    fn summary(&self) -> String;
}

pub struct NewsArticle {
    pub headline: String,
    pub content: String,
}

impl Summarizable for NewsArticle {
    fn summary(&self) -> String {
        format!("{} —— {}", self.headline, self.content)
    }
}

这样不同类型只要实现同一个 trait,就能被当成同一种东西处理——这就是 Rust 实现"多态"的方式。