|
|
@ -5,7 +5,7 @@
|
|
|
|
- 就像编译器大部分时候可以自动推导类型 <-> 一样,编译器大多数时候也可以自动推导生命周期
|
|
|
|
- 就像编译器大部分时候可以自动推导类型 <-> 一样,编译器大多数时候也可以自动推导生命周期
|
|
|
|
- 在多种类型存在时,编译器往往要求我们手动标明类型 <-> 当多个生命周期存在,且编译器无法推导出某个引用的生命周期时,就需要我们手动标明生命周期
|
|
|
|
- 在多种类型存在时,编译器往往要求我们手动标明类型 <-> 当多个生命周期存在,且编译器无法推导出某个引用的生命周期时,就需要我们手动标明生命周期
|
|
|
|
|
|
|
|
|
|
|
|
Rust 生命周期之所以难,是因为这个概念对于我们来说是全新的,没有其它编程语言的经验可以借鉴。当你觉得难的时候,不用过于担心,这个难对于所有人都是平等的,多点付出就能早点解决此拦路虎,同时本书也会尽力帮助大家减少学习难度(生命周期很可能是Rust中最难的部分)。
|
|
|
|
Rust 生命周期之所以难,是因为这个概念对于我们来说是全新的,没有其它编程语言的经验可以借鉴。当你觉得难的时候,不用过于担心,这个难对于所有人都是平等的,多点付出就能早点解决此拦路虎,同时本书也会尽力帮助大家减少学习难度(生命周期很可能是 Rust 中最难的部分)。
|
|
|
|
|
|
|
|
|
|
|
|
## 悬垂指针和生命周期
|
|
|
|
## 悬垂指针和生命周期
|
|
|
|
生命周期的主要作用是避免悬垂引用,它会导致程序引用了本不该引用的数据:
|
|
|
|
生命周期的主要作用是避免悬垂引用,它会导致程序引用了本不该引用的数据:
|
|
|
@ -29,16 +29,16 @@ Rust 生命周期之所以难,是因为这个概念对于我们来说是全新
|
|
|
|
|
|
|
|
|
|
|
|
此处 `r` 就是一个悬垂指针,它引用了提前被释放的变量 `x`,可以预料到,这段代码会报错:
|
|
|
|
此处 `r` 就是一个悬垂指针,它引用了提前被释放的变量 `x`,可以预料到,这段代码会报错:
|
|
|
|
```console
|
|
|
|
```console
|
|
|
|
error[E0597]: `x` does not live long enough // x活得不够久
|
|
|
|
error[E0597]: `x` does not live long enough // `x` 活得不够久
|
|
|
|
--> src/main.rs:7:17
|
|
|
|
--> src/main.rs:7:17
|
|
|
|
|
|
|
|
|
|
|
|
|
|
7 | r = &x;
|
|
|
|
7 | r = &x;
|
|
|
|
| ^^ borrowed value does not live long enough // 被借用的x活得不够久
|
|
|
|
| ^^ borrowed value does not live long enough // 被借用的 `x` 活得不够久
|
|
|
|
8 | }
|
|
|
|
8 | }
|
|
|
|
| - `x` dropped here while still borrowed // x在这里被丢弃,但是它依然还在被借用
|
|
|
|
| - `x` dropped here while still borrowed // `x` 在这里被丢弃,但是它依然还在被借用
|
|
|
|
9 |
|
|
|
|
9 |
|
|
|
|
10 | println!("r: {}", r);
|
|
|
|
10 | println!("r: {}", r);
|
|
|
|
| - borrow later used here // 对x的借用在此处被使用
|
|
|
|
| - borrow later used here // 对 `x` 的借用在此处被使用
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
在这里 `r` 拥有更大的作用域,或者说**活得更久**。如果 Rust 不阻止该垂悬引用的发生,那么当 `x` 被释放后,`r` 所引用的值就不再是合法的,会导致我们程序发生异常行为,且该异常行为有时候会很难被发现。
|
|
|
|
在这里 `r` 拥有更大的作用域,或者说**活得更久**。如果 Rust 不阻止该垂悬引用的发生,那么当 `x` 被释放后,`r` 所引用的值就不再是合法的,会导致我们程序发生异常行为,且该异常行为有时候会很难被发现。
|
|
|
@ -55,7 +55,7 @@ error[E0597]: `x` does not live long enough // x活得不够久
|
|
|
|
} // -+ |
|
|
|
|
} // -+ |
|
|
|
|
// |
|
|
|
|
// |
|
|
|
|
println!("r: {}", r); // |
|
|
|
|
println!("r: {}", r); // |
|
|
|
|
}
|
|
|
|
} // ---------+
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这段代码和之前的一模一样,唯一的区别在于增加了对变量生命周期的注释。这里,`r` 变量被赋予了生命周期 `'a`,`x` 被赋予了生命周期 `'b`,从图示上可以明显看出生命周期 `'b` 比 `'a` 小很多。
|
|
|
|
这段代码和之前的一模一样,唯一的区别在于增加了对变量生命周期的注释。这里,`r` 变量被赋予了生命周期 `'a`,`x` 被赋予了生命周期 `'b`,从图示上可以明显看出生命周期 `'b` 比 `'a` 小很多。
|
|
|
@ -126,7 +126,7 @@ help: consider introducing a named lifetime parameter // 考虑引入一个生
|
|
|
|
因此,这时就回到了文章开头说的内容:在存在多个引用时,编译器有时会无法自动推导生命周期,此时就需要我们手动去标注,通过为参数标注合适的生命周期来帮助编译器进行借用检查的分析。
|
|
|
|
因此,这时就回到了文章开头说的内容:在存在多个引用时,编译器有时会无法自动推导生命周期,此时就需要我们手动去标注,通过为参数标注合适的生命周期来帮助编译器进行借用检查的分析。
|
|
|
|
|
|
|
|
|
|
|
|
## 生命周期标注语法
|
|
|
|
## 生命周期标注语法
|
|
|
|
> 生命周期标注并不会改变任何引用的实际作用域 - 鲁迅
|
|
|
|
> 生命周期标注并不会改变任何引用的实际作用域 -- 鲁迅
|
|
|
|
|
|
|
|
|
|
|
|
鲁迅说过的话,总是值得重点标注,当你未来更加理解生命周期时,你才会发现这句话的精髓和重要!现在先简单记住,**标记的生命周期只是为了取悦编译器,让编译器不要难为我们**,记住了吗?没记住,再回头看一遍,这对未来你遇到生命周期问题时会有很大的帮助!
|
|
|
|
鲁迅说过的话,总是值得重点标注,当你未来更加理解生命周期时,你才会发现这句话的精髓和重要!现在先简单记住,**标记的生命周期只是为了取悦编译器,让编译器不要难为我们**,记住了吗?没记住,再回头看一遍,这对未来你遇到生命周期问题时会有很大的帮助!
|
|
|
|
|
|
|
|
|
|
|
@ -533,13 +533,13 @@ Bang,一个复杂的玩意儿被甩到了你面前,就问怕不怕?
|
|
|
|
|
|
|
|
|
|
|
|
就关键点稍微解释下:
|
|
|
|
就关键点稍微解释下:
|
|
|
|
|
|
|
|
|
|
|
|
- `'a: 'b`,是生命周期约束语法,跟泛型约束非常相似,用于说明`'a`必须比`'b`活得久
|
|
|
|
- `'a: 'b`,是生命周期约束语法,跟泛型约束非常相似,用于说明 `'a` 必须比 `'b` 活得久
|
|
|
|
- 为了实现这一点,必须把`'a`和`'b`都在同一个地方声明,你不能把`'a`在`impl`后面声明,而把`'b`在方法中声明
|
|
|
|
- 为了实现这一点,必须把 `'a` 和 `'b` 都在同一个地方声明,你不能把 `'a` 在 `impl` 后面声明,而把 `'b` 在方法中声明
|
|
|
|
|
|
|
|
|
|
|
|
总之,实现方法比想象中简单:加一个约束,就能暗示编译器,尽管引用吧,反正我想引用的内容比我活得久,爱咋咋地,我怎么都不会引用到无效的内容!
|
|
|
|
总之,实现方法比想象中简单:加一个约束,就能暗示编译器,尽管引用吧,反正我想引用的内容比我活得久,爱咋咋地,我怎么都不会引用到无效的内容!
|
|
|
|
|
|
|
|
|
|
|
|
## 静态生命周期
|
|
|
|
## 静态生命周期
|
|
|
|
在Rust中有一个非常特殊的生命周期,那就是 `'static`,拥有该生命周期的引用可以和整个程序活得一样久。
|
|
|
|
在 Rust 中有一个非常特殊的生命周期,那就是 `'static`,拥有该生命周期的引用可以和整个程序活得一样久。
|
|
|
|
|
|
|
|
|
|
|
|
在之前我们学过字符串字面量,提到过它是被硬编码进 Rust 的二进制文件中,因此这些字符串变量全部具有 `'static` 的生命周期:
|
|
|
|
在之前我们学过字符串字面量,提到过它是被硬编码进 Rust 的二进制文件中,因此这些字符串变量全部具有 `'static` 的生命周期:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
@ -586,8 +586,8 @@ where
|
|
|
|
依然是熟悉的配方 `longest`,但是多了一段废话: `ann`,因为要用格式化 `{}` 来输出 `ann`,因此需要它实现 `Display` 特征。
|
|
|
|
依然是熟悉的配方 `longest`,但是多了一段废话: `ann`,因为要用格式化 `{}` 来输出 `ann`,因此需要它实现 `Display` 特征。
|
|
|
|
|
|
|
|
|
|
|
|
## 总结
|
|
|
|
## 总结
|
|
|
|
我不知道支撑我一口气写完的勇气是什么,也许是不做完不爽夫斯基,也许是一些读者对本书的期待,不管如何,这章足足写了17000字,可惜不是写小说,不然肯定可以获取很多月票 :)
|
|
|
|
我不知道支撑我一口气写完的勇气是什么,也许是不做完不爽夫斯基,也许是一些读者对本书的期待,不管如何,这章足足写了 17000 字,可惜不是写小说,不然肯定可以获取很多月票 :)
|
|
|
|
|
|
|
|
|
|
|
|
从本章开始,最大的收获就是可以在结构体中使用引用类型了,说实话,为了引入这个特性,我已经憋了足足30章节。。
|
|
|
|
从本章开始,最大的收获就是可以在结构体中使用引用类型了,说实话,为了引入这个特性,我已经憋了足足 30 章节……
|
|
|
|
|
|
|
|
|
|
|
|
但是,还没完,是的,就算是将近2万字,生命周期的旅程依然没有完结,下一节将介绍一些关于生命周期的高级特性,这些特性你在其它中文书中目前还看不到的。
|
|
|
|
但是,还没完,是的,就算是将近两万字,生命周期的旅程依然没有完结,下一节将介绍一些关于生命周期的高级特性,这些特性你在其它中文书中目前还看不到的。
|
|
|
|