|
|
@ -1,6 +1,7 @@
|
|
|
|
# 认识生命周期
|
|
|
|
# 认识生命周期
|
|
|
|
|
|
|
|
|
|
|
|
生命周期,简而言之就是引用的有效作用域。在大多数时候,我们无需手动的声明生命周期,因为编译器可以自动进行推导,用类型来类比下:
|
|
|
|
生命周期,简而言之就是引用的有效作用域。在大多数时候,我们无需手动的声明生命周期,因为编译器可以自动进行推导,用类型来类比下:
|
|
|
|
|
|
|
|
|
|
|
|
- 就像编译器大部分时候可以自动推导类型 <-> 一样,编译器大多数时候也可以自动推导生命周期
|
|
|
|
- 就像编译器大部分时候可以自动推导类型 <-> 一样,编译器大多数时候也可以自动推导生命周期
|
|
|
|
- 在多种类型存在时,编译器往往要求我们手动标明类型 <-> 当多个生命周期存在,且编译器无法推导出某个引用的生命周期时,就需要我们手动标明生命周期
|
|
|
|
- 在多种类型存在时,编译器往往要求我们手动标明类型 <-> 当多个生命周期存在,且编译器无法推导出某个引用的生命周期时,就需要我们手动标明生命周期
|
|
|
|
|
|
|
|
|
|
|
@ -22,6 +23,7 @@ Rust 生命周期之所以难,是因为这个概念对于我们来说是全新
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这段代码有几点值得注意:
|
|
|
|
这段代码有几点值得注意:
|
|
|
|
|
|
|
|
|
|
|
|
- `let r;` 的声明方式貌似存在使用 `null` 的风险,实际上,当我们不初始化它就使用时,编译器会给予报错
|
|
|
|
- `let r;` 的声明方式貌似存在使用 `null` 的风险,实际上,当我们不初始化它就使用时,编译器会给予报错
|
|
|
|
- `r` 引用了内部花括号中的 `x` 变量,但是 `x` 会在内部花括号 `}` 处被释放,因此回到外部花括号后,`r` 会引用一个无效的 `x`
|
|
|
|
- `r` 引用了内部花括号中的 `x` 变量,但是 `x` 会在内部花括号 `}` 处被释放,因此回到外部花括号后,`r` 会引用一个无效的 `x`
|
|
|
|
|
|
|
|
|
|
|
@ -157,6 +159,7 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
需要注意的点如下:
|
|
|
|
需要注意的点如下:
|
|
|
|
|
|
|
|
|
|
|
|
- 和泛型一样,使用生命周期参数,需要先声明 `<'a>`
|
|
|
|
- 和泛型一样,使用生命周期参数,需要先声明 `<'a>`
|
|
|
|
- `x`、`y` 和返回值至少活得和 `'a` 一样久(因为返回值要么是 `x`,要么是 `y`)
|
|
|
|
- `x`、`y` 和返回值至少活得和 `'a` 一样久(因为返回值要么是 `x`,要么是 `y`)
|
|
|
|
|
|
|
|
|
|
|
@ -347,6 +350,7 @@ fn first_word(s: &str) -> &str {
|
|
|
|
该函数的参数和返回值都是引用类型,尽管我们没有显式的为其标注生命周期,编译依然可以通过。其实原因不复杂,**编译器为了简化用户的使用,运用了生命周期消除大法**。
|
|
|
|
该函数的参数和返回值都是引用类型,尽管我们没有显式的为其标注生命周期,编译依然可以通过。其实原因不复杂,**编译器为了简化用户的使用,运用了生命周期消除大法**。
|
|
|
|
|
|
|
|
|
|
|
|
对于 `first_word` 函数,它的返回值是一个引用类型,那么该引用只有两种情况:
|
|
|
|
对于 `first_word` 函数,它的返回值是一个引用类型,那么该引用只有两种情况:
|
|
|
|
|
|
|
|
|
|
|
|
- 从参数获取
|
|
|
|
- 从参数获取
|
|
|
|
- 从函数体内部新创建的变量获取
|
|
|
|
- 从函数体内部新创建的变量获取
|
|
|
|
|
|
|
|
|
|
|
@ -362,6 +366,7 @@ fn first_word<'a>(s: &'a str) -> &'a str {
|
|
|
|
生命周期消除的规则不是一蹴而就,而是伴随着 `总结-改善` 流程的周而复始,一步一步走到今天,这也意味着,该规则以后可能也会进一步增加,我们需要手动标注生命周期的时候也会越来越少,hooray!
|
|
|
|
生命周期消除的规则不是一蹴而就,而是伴随着 `总结-改善` 流程的周而复始,一步一步走到今天,这也意味着,该规则以后可能也会进一步增加,我们需要手动标注生命周期的时候也会越来越少,hooray!
|
|
|
|
|
|
|
|
|
|
|
|
在开始之前有几点需要注意:
|
|
|
|
在开始之前有几点需要注意:
|
|
|
|
|
|
|
|
|
|
|
|
- 消除规则不是万能的,若编译器不能确定某件事是正确时,会直接判为不正确,那么你还是需要手动标注生命周期
|
|
|
|
- 消除规则不是万能的,若编译器不能确定某件事是正确时,会直接判为不正确,那么你还是需要手动标注生命周期
|
|
|
|
- **函数或者方法中,参数的生命周期被称为 `输入生命周期`,返回值的生命周期被称为 `输出生命周期`**
|
|
|
|
- **函数或者方法中,参数的生命周期被称为 `输入生命周期`,返回值的生命周期被称为 `输出生命周期`**
|
|
|
|
|
|
|
|
|
|
|
@ -464,6 +469,7 @@ impl<'a> ImportantExcerpt<'a> {
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
其中有几点需要注意的:
|
|
|
|
其中有几点需要注意的:
|
|
|
|
|
|
|
|
|
|
|
|
- `impl` 中必须使用结构体的完整名称,包括 `<'a>`,因为*生命周期标注也是结构体类型的一部分*!
|
|
|
|
- `impl` 中必须使用结构体的完整名称,包括 `<'a>`,因为*生命周期标注也是结构体类型的一部分*!
|
|
|
|
- 方法签名中,往往不需要标注生命周期,得益于生命周期消除的第一和第三规则
|
|
|
|
- 方法签名中,往往不需要标注生命周期,得益于生命周期消除的第一和第三规则
|
|
|
|
|
|
|
|
|
|
|
@ -526,6 +532,7 @@ impl<'a: 'b, 'b> ImportantExcerpt<'a> {
|
|
|
|
Bang,一个复杂的玩意儿被甩到了你面前,就问怕不怕?
|
|
|
|
Bang,一个复杂的玩意儿被甩到了你面前,就问怕不怕?
|
|
|
|
|
|
|
|
|
|
|
|
就关键点稍微解释下:
|
|
|
|
就关键点稍微解释下:
|
|
|
|
|
|
|
|
|
|
|
|
- `'a: 'b`,是生命周期约束语法,跟泛型约束非常相似,用于说明`'a`必须比`'b`活得久
|
|
|
|
- `'a: 'b`,是生命周期约束语法,跟泛型约束非常相似,用于说明`'a`必须比`'b`活得久
|
|
|
|
- 为了实现这一点,必须把`'a`和`'b`都在同一个地方声明,你不能把`'a`在`impl`后面声明,而把`'b`在方法中声明
|
|
|
|
- 为了实现这一点,必须把`'a`和`'b`都在同一个地方声明,你不能把`'a`在`impl`后面声明,而把`'b`在方法中声明
|
|
|
|
|
|
|
|
|
|
|
@ -548,6 +555,7 @@ let s: &'static str = "我没啥优点,就是活得久,嘿嘿";
|
|
|
|
但是,话说回来,存在即合理,有时候,`'static` 确实可以帮助我们解决非常复杂的生命周期问题甚至是无法被手动解决的生命周期问题,那么此时就应该放心大胆的用,只要你确定:**你的所有引用的生命周期都是正确的,只是编译器太笨不懂罢了**。
|
|
|
|
但是,话说回来,存在即合理,有时候,`'static` 确实可以帮助我们解决非常复杂的生命周期问题甚至是无法被手动解决的生命周期问题,那么此时就应该放心大胆的用,只要你确定:**你的所有引用的生命周期都是正确的,只是编译器太笨不懂罢了**。
|
|
|
|
|
|
|
|
|
|
|
|
总结下:
|
|
|
|
总结下:
|
|
|
|
|
|
|
|
|
|
|
|
- 生命周期 `'static` 意味着能和程序活得一样久,例如字符串字面量和特征对象
|
|
|
|
- 生命周期 `'static` 意味着能和程序活得一样久,例如字符串字面量和特征对象
|
|
|
|
- 实在遇到解决不了的生命周期标注问题,可以尝试 `T: 'static`,有时候它会给你奇迹
|
|
|
|
- 实在遇到解决不了的生命周期标注问题,可以尝试 `T: 'static`,有时候它会给你奇迹
|
|
|
|
|
|
|
|
|
|
|
|