跳转至

引用为什么安全 — 生命周期、签名即承诺、共享与互斥

这一章回答全书最硬的问题: 上一章的规矩凭什么查得动?答案是「生命周期」—— 给每个引用指派一段「它安全可用」的区间,约束解得开就过、解不开就拒。 然后是这个机制的两大红利:函数签名会暴露函数体的行为,以及一整类 C++ 经典 bug 被灭种。

1. 顶层全景:一个约束求解问题

  每个引用类型 ⇒ 指派一个生命周期(程序中一段区间,编译期概念,运行时不存在)
      │
      ├─ 约束A(上限):被借的值 x 的寿命必须【包含】引用的寿命
      │     (x 先死了,引用就悬垂)
      ├─ 约束B(下限):存引用的变量 r 的寿命必须【被引用】包含
      │     (引用活得比 r 短,r 就成悬垂指针)
      ▼
  编译器:找一组同时满足所有约束的生命周期 —— 无解 ⇒ 拒绝编译

图说:本章所有例子——局部变量、函数、结构体——都是这两句约束的不同摆放。

2. 主线例子:最小的悬垂指针

{
    let r;
    {
        let x = 1;
        r = &x;              // 借 x
    }                        // x 在这里死
    assert_eq!(*r, 1);       // 错误:r 指向 x 原来的位置
}

Rust 的报错:x does not live long enough——x 死的时候还被借着1。 人眼看这错显而易见;值得看的是编译器怎么推出来的,因为同一套推理要扛住复杂得多的代码。

概念:生命周期(lifetime)。它是「一个引用可以安全使用的程序区间」——一条语句、一个表达式、 一个变量的作用域……原书特意强调:生命周期纯属编译期的想象;运行时引用就是一个地址, 生命周期是它类型的一部分,没有任何运行时表示2。

两句约束(用上面的例子):

  • 约束 A(给寿命封顶):&x 的寿命不能超过 x 的作用域,否则就是悬垂指针—— 「变量的寿命必须包含从它借来的引用的寿命」3;
  • 约束 B(给寿命托底):存进 r 的引用,寿命必须覆盖 r 从初始化到最后一次使用—— 「引用的寿命必须包含存它的变量的寿命」4。

在这个例子里:A 要求寿命在 x 死前结束,B 要求寿命活到外块结束。无解,拒绝5。 反过来,若 r 的最后一次使用挪到内块里,A、B 就有交集了,代码通过6。

3. 函数签名:承诺写在你看得见的地方

3.1 把引用存进全局,签名必须说

主线例子升级:函数 f 想把参数存进全局变量(Rust 里叫 static,程序启动就在、退出才走)。 写出来长这样:

static mut STASH: &i32 = &10;

fn f(p: &'static i32) {     // 关键在这一行
    unsafe { STASH = p; }
}

推理:STASH 活得和程序一样长,存进去的引用寿命也必须是 'static(整个程序); 而 f(p: &i32) 其实是 f<'a>(p: &'a i32) 的简写——「接受任何寿命的引用」。 「任何」当然包括短得只包住一次调用的寿命,存进 STASH 就悬垂——拒绝7。 想让函数只收能活到程序结束的引用?把签名改成 p: &'static i328。

作者让这一步的分量显形:从 f(p: &i32) 到 f(p: &'static i32),函数的签名忠实记录了函数体的行为—— 在 Rust 里,不把这个意图写进签名,函数就写不出来。「函数签名总是暴露函数体的行为」9。

推论同样值钱:看到 g(p: &i32),不用看函数体就知道 g 不会把 p 存到活得比这次调用久的地方; 想确认一次调用的安全性,只读签名就够10。

3.2 返回引用:寿命绑回参数

函数拿了引用、又返回引用,返回的指向谁?Rust 的省略规则假定:参数和返回值同一寿命。 fn smallest(v: &[i32]) -> &i32 展开是 fn smallest<'a>(v: &'a [i32]) -> &'a i3211。

后果:返回值借自参数所指的数据。把这个结果存得比数据源还久,无解,拒绝:

let s;
{
    let parabola = [9, 4, 1, 0, 1, 4, 9];
    s = smallest(&parabola);
}                             // parabola 死
assert_eq!(*s, 0);            // 错误:s 指向已死的数组元素

把 s 挪进内块,约束就有解了12。

3.3 结构体里的引用:寿命参数传染

struct S { r: &i32 }   // 错误:missing lifetime specifier

引用藏进结构体,安全约束不会 magically 消失——它们必须传染到 S 自己身上。 要么写死 'static(很受限:只能借程序全程存活的东西),要么给类型自己的寿命参数:

struct S<'a> { r: &'a i32 }

每个 S 值的 'a 由用法决定:存进 r 的引用寿命要包住 'a;'a 要撑住 S 活的地方13。 而 S 再被包进 D,D 也得声明寿命参数(或者只用 S<'static>)——传染到底14。

为什么逼你写?Rust 早期版本自动给每个引用造隐形寿命,开发者发现很迷惑: 知道「一个值从另一个值借了东西」对排错极有帮助,所以改成显式15。 类型签名由此也成为承诺:fn parse_record<'i>(input: &'i [u8]) -> Record<'i> 不用看 Record 的定义就知道——Record 里的引用指向传进来的 buffer,不会指向别处16。

3.4 完整代码重放:结构体传染的两级现场

把书里 S、D 两个结构体的完整推演走一遍,体会「传染」是怎么逐层发生的40。

第一级:引用直接作字段。

struct S {
    r: &i32,     // 错误:missing lifetime specifier
}

错误的原因上一节说了:引用藏进结构体,安全约束必须跟着传染到 S 身上,而传染的载体 就是 S 自己的寿命参数。修正:

struct S<'a> {
    r: &'a i32,
}

第二级:S 被 D 包进去。

struct D {
    s: S,        // 错误:又是 missing lifetime specifier
}

S 本身带参数,D 就必须表态:要么只用 S<'static>(那 D 就不能借局部变量了, 但 D 自己无任何限制),要么 D 也领一个参数传给 S:

struct D<'a> {
    s: S<'a>,
}

两级推演,规则一条:谁的类型里出现带参数的引用(或带参数的 S),谁就得带参数。 没有例外、没有「编译器帮我记着」——早期的 Rust 曾自动记,被开发者否了, 理由是「知道一个值从另一个值借了东西,对排错太重要了」41。

3.5 完整代码重放:返回引用绑回参数

书上这个函数很小,却是「签名即承诺」的最佳标本:

// v 至少要有一个元素
fn smallest(v: &[i32]) -> &i32 {
    let mut s = &v[0];
    for r in &v[1..] {
        if *r < *s { s = r; }
    }
    s
}

省略规则把它补成 fn smallest<'a>(v: &'a [i32]) -> &'a i32—— 输入切片和返回引用共用一条命。这个签名向外声明两件事: 调用方传进来的切片,返回值指向它的内部;调用方不能让返回值活得比切片久。 两种常见的死法因此编译不过:

let s;
{
    let parabola = [9, 4, 1, 0, 1, 4, 9];
    s = smallest(&parabola);
}                        // parabola 在这里死
assert_eq!(*s, 0);       // 错误:s 指向死掉的数组元素

把 s 挪进内块,约束立刻有解——编译器只看「值的寿命 ⊇ 引用寿命 ⊇ 变量寿命」 能不能同时成立,不关心代码写起来「显然没问题」42。

3.6 调用方视角:编译器替你挑最短的寿命

签名是承诺,调用是履约。看同一个 g 的两个调用47:

fn g<'a>(p: &'a i32) { /* …… */ }

let x = 10;
g(&x);

从 g 的签名我们知道:它不会把 p 存到活得比这次调用久的地方——任何包住调用的寿命 都满足 'a。所以编译器给 &x 挑最短的候选:恰好包住这次调用的那一点。 约束全过:不比 x 活得长(它在 x 作用域内),也不比调用短(覆盖了整个调用)。 调用方一个寿命字都不用写48。

反过来,把 &x 传给前面那个存全局的 f(p: &'static i32) 呢? &x 最多活到 x 死,而 f 要求活到程序结束——无解,拒。 同一段 &x,函数签名决定它能不能去——这就是「签名即承诺」在调用侧的读法49。

3.7 statics 的两条军规

那个存引用的全局变量例子顺带交代了 Rust 静态变量(static,程序启动就在、 退出才走的值)的两条军规50:

  1. 必须初始化——没有「先声明后赋值」的余地;
  2. 可变的静态量天生线程不安全(任何线程随时能摸它,单线程下也有重入问题), 只能在 unsafe 块里访问。

第二条和第 5 节的并发结论对上号:共享 + 可变 = 危险,这条等式在静态量上也不打折扣。

借的是大结构的一部分:约束自动上溯

规则对「借一部分」自动生效,不用你额外声明。借向量一个元素的引用:

let v = vec![1, 2, 3];
let r = &v[1];

v 拥有向量,向量拥有元素,所以「v 的寿命 ⊇ 引用的寿命」这条约束, 自动沿着拥有链传导到元素身上。同样,把引用存进数据结构,引用的寿命就必须包住 整个结构的寿命——若你造了一个装满引用的向量,每条引用的寿命都得盖住那个向量51。

这就是约束求解不需要「特殊情形」的原因:所有规则都是同一句「值的寿命 ⊇ 引用寿命」 在不同摆放下的复读,向量、结构体、HashMap、嵌套三层,一视同仁。

4. 省略:三种默认规则

并不是处处都要写寿命。Rust 的省略规则17:

情形 默认
参数里的引用 每个位置一个独立的新寿命
只有一个输入寿命时,返回值里的寿命 就是那个输入寿命
&self 方法(方法是挂在类型上的函数,self 就是被调的那个值) 返回值寿命一律取 &self 的

fn first_third(point: &[i32; 3]) -> (&i32, &i32) 自动补成 <'a>(point: &'a [i32; 3]) -> (&'a i32, &'a i32)18; find_by_prefix(&self, prefix: &str) -> Option<&String> 自动补成 Option<&'a String>—— 「你从 self 里借东西」19。

约束写太紧怎么办?一个结构体两个引用共用一个 'a,会被迫同生共死; 拆成 <'a, 'b> 就各活各的20。函数签名同理。 作者的建议很务实:先写最简单的,编译器拒绝再放宽——反正不安全就编译不过,等它告诉你21。

4.1 过紧现场:两个引用被迫同生共死

省略规则省心,但有一种经典的「太紧」值得亲手踩一遍43。

struct S<'a> {
    x: &'a i32,
    y: &'a i32,   // 与 x 共用一条命 'a
}

let x = 10;
let r;
{
    let y = 20;
    {
        let s = S { x: &x, y: &y };
        r = s.x;          // 把 x 的那份引用留下来
    }                      // s 死
}                          // y 死
println!("{r}");          // r 指向 x,活着——这段代码其实没问题

但 Rust 会拒:complains that y does not live long enough。推理:

  1. 两个字段共用一个 'a,编译器得给 s.x 和 s.y 找一条同时管用的寿命;
  2. r = s.x 要求 'a 包住 r 的寿命(r 用到外块结束);
  3. s.y = &y 要求 'a 不超过 y 的寿命(y 在内块死)。

要 'a 比 y 短、又比 r 长——不存在这样的区间44。

修复:给两个字段各发一条命。

struct S<'a, 'b> {
    x: &'a i32,
    y: &'b i32,
}

'a 取 r 的寿命,'b 取 s 的寿命,各不相欠,编译通过。 函数签名同款:参数挤一个 'a 会过度约束调用方,拆开 'a, 'b 就松了45。

代价作者也明说了:寿命越多,签名越难读。他们的习惯是先最简、报错再松—— 反正报的都是编译错误,等编译器开口毫无风险46。

5. 共享与互斥:一整类 bug 的灭种

5.1 两条规则的正式版

回到上一章的规矩,这里给机制上的理由。悬垂还有别的造法:值被搬走了,引用还活着:

let v = vec![4, 8, 19, 27, 34, 10];
let r = &v;
let aside = v;      // 搬走 v,r 悬垂
r[0];               // 错误:cannot move out of `v` because it is borrowed

v 明明在 r 的整段寿命里都「在作用域内」——但值被搬走了,等于死了却没退出作用域。 所以共享引用活着时,它到被借值之间的整条所有权路径都被冻结:不许赋值、不许移动22。

第二条路线的名场面:往正在指着的向量里加元素。

let mut wave = vec![0.0, 1.0, 0.0, -1.0];
extend(&mut wave, &wave);   // 把 wave 自己追加给自己

Casual 看没问题。但如果 wave 容量不够,push 会换一块更大的缓冲,旧缓冲释放—— 而 &wave 指向的旧缓冲上的切片立刻悬垂。这类 bug 的阴险处:测试时容量总是够,永不触发23。 C++ 的 std::vector 规范白纸黑字:「重分配使指向元素的引用、指针、迭代器全部失效」; Java 的 Hashtable 结构被改时迭代器直接抛 ConcurrentModificationException—— 家家都有这坑,只是别的语言在运行时撞见,Rust 在编译时拒绝:extend(&mut wave, &wave) 同时借了可变和共享,互斥24。

5.2 两条规则的原话

原书把它们写成两条,值得原文照录其义:

共享访问即只读访问。 共享引用借着的值,从它能到达的一切路径都被冻结: 没有任何活的可变引用通向它,主人也只读。

可变访问即排他访问。 可变引用活着时,通向它的值不存在第二条可用路径—— 唯一能与之共存的引用,是从这个可变引用自己再借出去的25。

注意范围:冻结的不只是被借的值本身,而是指向它的整条路径和它能到达的一切。 你借了 HashMap 里一个键的共享引用,整个 HashMap 直到引用死前都拒绝可变借用26。

5.3 灭种名单

书里点名的三个 C++ 经典 bug,其实是同一类:

C++ 的 bug 形状 Rust 的下场
自赋值翻车:f = f 把正要复制的资源先关了 「以为在读 A、其实在改 A」 clone_from(&mut f, &f) 直接编译失败
迭代器失效:边遍历边改容器 同上 共享借用期间禁止可变借用
memcpy/strcpy 源和目标重叠 同上 可变访问排他,重叠借不出来

作者的总结:两个经典 C++ bug——没处理自赋值、用了失效迭代器——是同一个底层 bug 的两种形态; 让可变访问排他,Rust 把这一整类日常错误挡在门外27。

5.4 并发:数据竞争按构造消失

这是两条规则最大的红利。数据竞争的定义:某个值既可变、又被跨线程共享—— 「恰好就是 Rust 引用规则消灭的那个形状」。所以一个不用 unsafe 的 Rust 并发程序, 按构造( by construction)无数据竞争28。第 1 章那个「编译过即无数据竞争」的承诺,到这里才有了根据。

6. 设计层面的余波:sea of objects 的减速带

垃圾回收时代以来,程序默认的架构是对象之海:什么都指什么,几年后没人说得清依赖关系, 「几年之后,你会有充分的理由重写一遍(放 AC/DC 的《通往地狱的高速公路》)」——原书的原话带刺29。

Rust 的所有权模型给这条高速公路装了减速带:在 Rust 里造一个环(两个值互相指)要费点劲—— 得动用 Rc 加内部可变性(还没讲的特性);Rust 偏爱指针、所有权、数据流(数据在部件之间的流动)单向穿过系统的一棵树30。

作者给读者的警告也在这里:读完本章别冲出去用 Rc 连一片「结构体之海」, 复刻你熟悉的那套面向对象反模式——Rust 会让你碰钉子。药方是做前期设计,建更好的程序31。 全书的态度浓缩为一句:「Rust 就是把理解你的程序的痛苦,从未来挪到现在」32。

7. 作者的判断与证据

  • 约束求解的推理过程(§2)是机制描述,可信;「运行时零成本」由「生命周期无运行时表示」直接成立2。
  • 「自赋值与迭代器失效是同一种 bug」是作者的归纳,有 C++ 规范原文撑腰(引文见 §5.1)23。
  • 「等编译器告诉你,再放宽签名」是作者自己的工程习惯,被写成建议21。
  • 「痛苦挪到现在」是立场句,同时是准确的工程观察——前期设计成本换后期调试成本。

8. 边界与局限

  • 省略规则有三条默认,但高阶场景(闭包返回引用、impl 块里的复杂签名)仍需手写,本章只给了入门集;
  • &mut 重借(reborrow)在嵌套调用里的精确规则没有展开;
  • 「按构造无数据竞争」只覆盖无 unsafe 的代码;unsafe 块里一切靠自己;
  • sea of objects 的讨论是定性分析,没有案例数据。

9. 可带走的

主线例子一行版: r = &x 后 x 先死 → 约束 A(值包引用)与约束 B(引用包变量)无解 → 拒绝编译。

  1. 生命周期 = 引用可安全使用的程序区间;纯编译期概念,运行时引用只是地址;
  2. 约束两句:被借值的寿命 ⊇ 引用寿命;引用寿命 ⊇ 存引用变量的寿命;
  3. 函数签名必须如实记录引用去向:存全局就得写 &'static,否则写不出来;
  4. 读签名即知函数行为:返回引用绑回参数;不看函数体也知道它不会把参数藏起来;
  5. 结构体含引用 ⇒ 类型必须有寿命参数,且传染到外层类型——签名即承诺;
  6. 省略规则:单输入寿命绑返回值;&self 方法返回值绑 self;多输入寿命要写;
  7. 写太紧就拆 'a, 'b;先简后繁,等编译器拒绝再放宽;
  8. 共享借用在,整条路径冻结(不许移动、可变);可变借用在,排他;
  9. 自赋值、迭代器失效、memcpy 重叠是同一类 bug——「可变即排他」全部灭种;
  10. 数据竞争的定义形状被引用规则预先排除:无 unsafe ⟹ 并发按构造安全。

10. 每个类型都有寿命——多数类型是 'static

一个容易滑过去的普遍化(原书第 599 段):不只是引用和有引用的结构体有寿命,Rust 里每个类型都有。 大多数类型的寿命是 'static——意思是「这种类型的值想活多久活多久」: Vec<i32> 自包含,不必赶在哪个变量死之前先死;i32、String 也一样33。

但只要类型里掺了引用,寿命就矮一截:Vec<&'a i32> 的寿命被 'a 压着头, 必须在它指的那些整数还活着的时候先死掉。给类型标寿命,等于在类型上挂了张「此物有保质期」的标签, 编译器按标签安排检查的顺序34。

回看签名即承诺那条:一个类型有没有寿命参数、参数叫什么,就是它含不含借用的公开声明。 Record<'i> 一眼能看出它借了输入缓冲;i32 一眼能看出它谁也没借。这就是为什么 Rust 坚持让寿命参数写在明面上——它不只是检查的需要,也是读代码时的路标35。

11. 省略规则的三个完整案例

规则是抽象的,原书给了三个展开的案例,值得各看一眼。

案例一:多个参数各自独立。 fn sum_r_xy(r: &i32, s: S) -> i32 展开成 fn sum_r_xy<'a, 'b, 'c>(r: &'a i32, s: S<'b, 'c>) -> i32—— 每个需要寿命的位置都分到一个全新、互不相干的寿命,谁也不欠谁36。

案例二:单输入寿命绑返回值。 fn first_third(point: &[i32; 3]) -> (&i32, &i32) 展开成 fn first_third<'a>(point: &'a [i32; 3]) -> (&'a i32, &'a i32)。 返回值里的两个引用,都指向参数的数组——既然只有一个候选,就全绑给它37。

案例三:&self 一锤定音。 方法是挂在类型上的函数,self 就是被调的那个值 (Rust 里的 this)。fn find_by_prefix(&self, prefix: &str) -> Option<&String> 展开成 fn find_by_prefix<'a, 'b>(&'a self, prefix: &'b str) -> Option<&'a String>: 返回值不绑 prefix,绑 self——「你从自己身上借东西」。 这个默认非常符合直觉:按前缀查表,找到的自然是表里(不是前缀里)的字符串38。

三条记不住没关系,作者给了护身策略:先写最简形态,编译器拒绝再补—— 寿命写错了永远是编译错误,不是运行时事故,等编译器开口是最省力的学法39。

12. 原文地图

主题 原书章 原文位置
生命周期的定义 Chapter 4. References text/14-ch04-chapter-4-references.txt:339(搜「figments」)
约束 A:值包引用 Chapter 4. References text/14-ch04-chapter-4-references.txt:345(搜「contain or enclose」)
约束 B:引用包变量 Chapter 4. References text/14-ch04-chapter-4-references.txt:351(搜「contain or enclose」)
最小悬垂例子与报错 Chapter 4. References text/14-ch04-chapter-4-references.txt:326(搜「does not live long enough」)
'static 与全局变量 Chapter 4. References text/14-ch04-chapter-4-references.txt:405(搜「'static lifetime」)
签名暴露行为 Chapter 4. References text/14-ch04-chapter-4-references.txt:432(搜「signature always exposes」)
smallest 返回引用 Chapter 4. References text/14-ch04-chapter-4-references.txt:465(搜「smallest」) · text/14-ch04-chapter-4-references.txt:486(搜「same lifetime」)
结构体缺寿命报错 Chapter 4. References text/14-ch04-chapter-4-references.txt:526(搜「missing lifetime specifier」)
传染到外层类型 Chapter 4. References text/14-ch04-chapter-4-references.txt:552(搜「not adequate」)
为什么逼你写寿命 Chapter 4. References text/14-ch04-chapter-4-references.txt:597(搜「confusing」)
拆寿命参数 Chapter 4. References text/14-ch04-chapter-4-references.txt:601(搜「Distinct Lifetime」) · text/14-ch04-chapter-4-references.txt:642(搜「independent」)
等编译器拒绝再放宽 Chapter 4. References text/14-ch04-chapter-4-references.txt:652(搜「loosen restrictions」)
省略规则 Chapter 4. References text/14-ch04-chapter-4-references.txt:673(搜「single lifetime」) · text/14-ch04-chapter-4-references.txt:685(搜「self parameter」)
向量扩容失效引用 Chapter 4. References text/14-ch04-chapter-4-references.txt:771(搜「eight elements」) · text/14-ch04-chapter-4-references.txt:775(搜「invalidates」)
两条规则原文 Chapter 4. References text/14-ch04-chapter-4-references.txt:796(搜「read-only access」) · text/14-ch04-chapter-4-references.txt:798(搜「exclusive access」)
自赋值 bug 灭种 Chapter 4. References text/14-ch04-chapter-4-references.txt:908(搜「self-assignment」) · text/14-ch04-chapter-4-references.txt:908(搜「overlap」)
数据竞争按构造消失 Chapter 4. References text/14-ch04-chapter-4-references.txt:910(搜「by construction」)
sea of objects 与减速带 Chapter 4. References text/14-ch04-chapter-4-references.txt:936(搜「sea of objects」) · text/14-ch04-chapter-4-references.txt:946(搜「speed bump」)
痛苦挪到现在 Chapter 4. References text/14-ch04-chapter-4-references.txt:952(搜「transferring the pain」)
每个类型都有寿命 Chapter 4. References text/14-ch04-chapter-4-references.txt:599(搜「Every type」)
结构体传染的完整现场 Chapter 4. References text/14-ch04-chapter-4-references.txt:526(搜「missing lifetime specifier」)
smallest 返回引用 Chapter 4. References text/14-ch04-chapter-4-references.txt:465(搜「smallest」)
两个引用共用一个寿命的过紧现场 Chapter 4. References text/14-ch04-chapter-4-references.txt:601(搜「Distinct」)
调用方视角:最短寿命 Chapter 4. References text/14-ch04-chapter-4-references.txt:447(搜「smallest possible lifetime」)
statics 的两条军规 Chapter 4. References text/14-ch04-chapter-4-references.txt:382(搜「Every static」)
借大结构的一部分:约束上溯 Chapter 4. References text/14-ch04-chapter-4-references.txt:368(搜「owns the vector」)
省略规则三案例 Chapter 4. References text/14-ch04-chapter-4-references.txt:665(搜「sum_r_xy」) · text/14-ch04-chapter-4-references.txt:675(搜「first_third」) · text/14-ch04-chapter-4-references.txt:694(搜「find_by_prefix」)

  1. 出处:「Chapter 4. References」第 321–335 段(text/14-ch04-chapter-4-references.txt:321-335,搜「does not live long enough」)。 ↩

  2. 出处:「Chapter 4. References」第 339 段(text/14-ch04-chapter-4-references.txt:339,搜「figments」)。 ↩↩

  3. 出处:「Chapter 4. References」第 345 段(text/14-ch04-chapter-4-references.txt:345,搜「contain or enclose」)。 ↩

  4. 出处:「Chapter 4. References」第 351 段(text/14-ch04-chapter-4-references.txt:351,搜「contain or enclose」)。 ↩

  5. 出处:「Chapter 4. References」第 357 段(text/14-ch04-chapter-4-references.txt:357,搜「contradictory」)。 ↩

  6. 出处:「Chapter 4. References」第 359–361 段(text/14-ch04-chapter-4-references.txt:359-361,搜「meets the constraints」)。 ↩

  7. 出处:「Chapter 4. References」第 395–413 段(text/14-ch04-chapter-4-references.txt:395-413,搜「may not live long enough」)。 ↩

  8. 出处:「Chapter 4. References」第 415–430 段(text/14-ch04-chapter-4-references.txt:415-430,搜「compiles just fine」)。 ↩

  9. 出处:「Chapter 4. References」第 432 段(text/14-ch04-chapter-4-references.txt:432,搜「signature always exposes」)。 ↩

  10. 出处:「Chapter 4. References」第 434 段(text/14-ch04-chapter-4-references.txt:434,搜「no need to look」)。 ↩

  11. 出处:「Chapter 4. References」第 462–475 段(text/14-ch04-chapter-4-references.txt:462-475,搜「same lifetime」)。 ↩

  12. 出处:「Chapter 4. References」第 477–504 段(text/14-ch04-chapter-4-references.txt:477-504,搜「parabola」)。 ↩

  13. 出处:「Chapter 4. References」第 524–547 段(text/14-ch04-chapter-4-references.txt:524-547,搜「fresh lifetime」)。 ↩

  14. 出处:「Chapter 4. References」第 549–587 段(text/14-ch04-chapter-4-references.txt:549-587,搜「not adequate」)。 ↩

  15. 出处:「Chapter 4. References」第 597 段(text/14-ch04-chapter-4-references.txt:597,搜「confusing」)。 ↩

  16. 出处:「Chapter 4. References」第 593–595 段(text/14-ch04-chapter-4-references.txt:593-595,搜「parse_record」)。 ↩

  17. 出处:「Chapter 4. References」第 656–671 段(text/14-ch04-chapter-4-references.txt:656-671,搜「omit」)。 ↩

  18. 出处:「Chapter 4. References」第 673–681 段(text/14-ch04-chapter-4-references.txt:673-681,搜「single lifetime」)。 ↩

  19. 出处:「Chapter 4. References」第 685–708 段(text/14-ch04-chapter-4-references.txt:685-708,搜「self parameter」)。 ↩

  20. 出处:「Chapter 4. References」第 603–642 段(text/14-ch04-chapter-4-references.txt:603-642,搜「independent lifetimes」)。 ↩

  21. 出处:「Chapter 4. References」第 652 段(text/14-ch04-chapter-4-references.txt:652,搜「loosen restrictions」)。 ↩↩

  22. 出处:「Chapter 4. References」第 714–727 段(text/14-ch04-chapter-4-references.txt:714-727,搜「moved elsewhere」)。 ↩

  23. 出处:「Chapter 4. References」第 769–779 段(text/14-ch04-chapter-4-references.txt:769-779,搜「invalidates」)。 ↩↩

  24. 出处:「Chapter 4. References」第 781–792 段(text/14-ch04-chapter-4-references.txt:781-792,搜「cannot borrow」)。 ↩

  25. 出处:「Chapter 4. References」第 794–799 段(text/14-ch04-chapter-4-references.txt:794-799,搜「read-only access」与「exclusive access」)。 ↩

  26. 出处:「Chapter 4. References」第 848 段(text/14-ch04-chapter-4-references.txt:848,搜「HashMap」)。 ↩

  27. 出处:「Chapter 4. References」第 908 段(text/14-ch04-chapter-4-references.txt:908,搜「same underlying kind of bug」)。 ↩

  28. 出处:「Chapter 4. References」第 910 段(text/14-ch04-chapter-4-references.txt:910,搜「by construction」)。 ↩

  29. 出处:「Chapter 4. References」第 936–942 段(text/14-ch04-chapter-4-references.txt:936-942,搜「sea of objects」)。 ↩

  30. 出处:「Chapter 4. References」第 946 段(text/14-ch04-chapter-4-references.txt:946,搜「speed bump」)。 ↩

  31. 出处:「Chapter 4. References」第 950 段(text/14-ch04-chapter-4-references.txt:950,搜「sea of structs」)。 ↩

  32. 出处:「Chapter 4. References」第 952 段(text/14-ch04-chapter-4-references.txt:952,搜「transferring the pain」)。 ↩

  33. 出处:「Chapter 4. References」第 599 段(text/14-ch04-chapter-4-references.txt:599,搜「Every type」)。 ↩

  34. 出处:「Chapter 4. References」第 599 段(text/14-ch04-chapter-4-references.txt:599,搜「must be dropped」)。 ↩

  35. 出处:「Chapter 4. References」第 589–597 段(text/14-ch04-chapter-4-references.txt:589-597,搜「roadside」)。「这就是 Rust 要求含引用的类型显式带寿命参数的原因」一节。 ↩

  36. 出处:「Chapter 4. References」第 665–671 段(text/14-ch04-chapter-4-references.txt:665-671,搜「distinct lifetime」)。 ↩

  37. 出处:「Chapter 4. References」第 673–681 段(text/14-ch04-chapter-4-references.txt:673-681,搜「single lifetime」)。 ↩

  38. 出处:「Chapter 4. References」第 685–708 段(text/14-ch04-chapter-4-references.txt:685-708,搜「self parameter」)。 ↩

  39. 出处:「Chapter 4. References」第 652 段(text/14-ch04-chapter-4-references.txt:652,搜「perfectly acceptable tactic」)。 ↩

  40. 出处:「Chapter 4. References」第 508–587 段(text/14-ch04-chapter-4-references.txt:508-587,搜「expected lifetime parameter」)。 ↩

  41. 出处:「Chapter 4. References」第 597 段(text/14-ch04-chapter-4-references.txt:597,搜「confusing」)。 ↩

  42. 出处:「Chapter 4. References」第 462–504 段(text/14-ch04-chapter-4-references.txt:462-504,搜「smallest」)。 ↩

  43. 出处:「Chapter 4. References」第 601–642 段(text/14-ch04-chapter-4-references.txt:601-642,搜「does not create any dangling」)。 ↩

  44. 出处:「Chapter 4. References」第 625–633 段(text/14-ch04-chapter-4-references.txt:625-633,搜「impossible to satisfy」)。 ↩

  45. 出处:「Chapter 4. References」第 644–650 段(text/14-ch04-chapter-4-references.txt:644-650,搜「looser」)。 ↩

  46. 出处:「Chapter 4. References」第 652 段(text/14-ch04-chapter-4-references.txt:652,搜「perfectly acceptable tactic」)。 ↩

  47. 出处:「Chapter 4. References」第 436–458 段(text/14-ch04-chapter-4-references.txt:436-458,搜「smallest possible lifetime」)。 ↩

  48. 出处:「Chapter 4. References」第 447–449 段(text/14-ch04-chapter-4-references.txt:447-449,搜「You only need to worry」)。 ↩

  49. 出处:「Chapter 4. References」第 453–458 段(text/14-ch04-chapter-4-references.txt:453-458,搜「no way to satisfy」)。 ↩

  50. 出处:「Chapter 4. References」第 380–386 段(text/14-ch04-chapter-4-references.txt:380-386,搜「Every static」)。 ↩

  51. 出处:「Chapter 4. References」第 363–368 段(text/14-ch04-chapter-4-references.txt:363-368,搜「owns the vector」)。 ↩