|
|
@ -7,7 +7,7 @@
|
|
|
|
- 就像编译器大部分时候可以自动推导类型 <-> 一样,编译器大多数时候也可以自动推导生命周期
|
|
|
|
- 就像编译器大部分时候可以自动推导类型 <-> 一样,编译器大多数时候也可以自动推导生命周期
|
|
|
|
- 在多种类型存在时,编译器往往要求我们手动标明类型 <-> 当多个生命周期存在,且编译器无法推导出某个引用的生命周期时,就需要我们手动标明生命周期
|
|
|
|
- 在多种类型存在时,编译器往往要求我们手动标明类型 <-> 当多个生命周期存在,且编译器无法推导出某个引用的生命周期时,就需要我们手动标明生命周期
|
|
|
|
|
|
|
|
|
|
|
|
Rust 生命周期之所以难,是因为这个概念对于我们来说是全新的,没有其它编程语言的经验可以借鉴。当你觉得难的时候,不用过于担心,这个难对于所有人都是平等的,多点付出就能早点解决此拦路虎,同时本书也会尽力帮助大家减少学习难度(生命周期很可能是 Rust 中最难的部分)。
|
|
|
|
Rust 生命周期之所以难,是因为这个概念对于我们来说是全新的,没有其它编程语言的经验可以借鉴。当你觉得难的时候,不用过于担心,这个难对于所有人都是平等的,多点付出就能早点解决此拦路虎,同时本书也会尽力帮助大家减少学习难度(生命周期很可能是 Rust 中最难的部分)。
|
|
|
|
|
|
|
|
|
|
|
|
## 悬垂指针和生命周期
|
|
|
|
## 悬垂指针和生命周期
|
|
|
|
|
|
|
|
|
|
|
@ -130,7 +130,7 @@ help: consider introducing a named lifetime parameter // 考虑引入一个生
|
|
|
|
|
|
|
|
|
|
|
|
不过说来尴尬,就这个函数而言,我们也不知道返回值到底引用哪个,因为一个分支返回 `x`,另一个分支返回 `y`...这可咋办?先来分析下。
|
|
|
|
不过说来尴尬,就这个函数而言,我们也不知道返回值到底引用哪个,因为一个分支返回 `x`,另一个分支返回 `y`...这可咋办?先来分析下。
|
|
|
|
|
|
|
|
|
|
|
|
我们在定义该函数时,首先无法知道传递给函数的具体值,因此到底是 `if` 还是 `else` 被执行,无从得知。其次,传入引用的具体生命周期也无法知道,因此也不能像之前的例子那样通过分析生命周期来确定引用是否有效。同时,编译器的借用检查也无法推导出返回值的生命周期,因为它不知道 `x` 和 `y` 的生命周期跟返回值的生命周期之间的关系是怎样的(说实话,人都搞不清,何况编译器这个大聪明)。
|
|
|
|
我们在定义该函数时,首先无法知道传递给函数的具体值,因此到底是 `if` 还是 `else` 被执行,无从得知。其次,传入引用的具体生命周期也无法知道,因此也不能像之前的例子那样通过分析生命周期来确定引用是否有效。同时,编译器的借用检查也无法推导出返回值的生命周期,因为它不知道 `x` 和 `y` 的生命周期跟返回值的生命周期之间的关系是怎样的(说实话,人都搞不清,何况编译器这个大聪明)。
|
|
|
|
|
|
|
|
|
|
|
|
因此,这时就回到了文章开头说的内容:在存在多个引用时,编译器有时会无法自动推导生命周期,此时就需要我们手动去标注,通过为参数标注合适的生命周期来帮助编译器进行借用检查的分析。
|
|
|
|
因此,这时就回到了文章开头说的内容:在存在多个引用时,编译器有时会无法自动推导生命周期,此时就需要我们手动去标注,通过为参数标注合适的生命周期来帮助编译器进行借用检查的分析。
|
|
|
|
|
|
|
|
|
|
|
@ -175,9 +175,9 @@ fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
|
|
|
|
需要注意的点如下:
|
|
|
|
需要注意的点如下:
|
|
|
|
|
|
|
|
|
|
|
|
- 和泛型一样,使用生命周期参数,需要先声明 `<'a>`
|
|
|
|
- 和泛型一样,使用生命周期参数,需要先声明 `<'a>`
|
|
|
|
- `x`、`y` 和返回值至少活得和 `'a` 一样久(因为返回值要么是 `x`,要么是 `y`)
|
|
|
|
- `x`、`y` 和返回值至少活得和 `'a` 一样久(因为返回值要么是 `x`,要么是 `y`)
|
|
|
|
|
|
|
|
|
|
|
|
该函数签名表明对于某些生命周期 `'a`,函数的两个参数都至少跟 `'a` 活得一样久,同时函数的返回引用也至少跟 `'a` 活得一样久。实际上,这意味着返回值的生命周期与参数生命周期中的较小值一致:虽然两个参数的生命周期都是标注了 `'a`,但是实际上这两个参数的真实生命周期可能是不一样的(生命周期 `'a` 不代表生命周期等于 `'a`,而是大于等于 `'a`)。
|
|
|
|
该函数签名表明对于某些生命周期 `'a`,函数的两个参数都至少跟 `'a` 活得一样久,同时函数的返回引用也至少跟 `'a` 活得一样久。实际上,这意味着返回值的生命周期与参数生命周期中的较小值一致:虽然两个参数的生命周期都是标注了 `'a`,但是实际上这两个参数的真实生命周期可能是不一样的(生命周期 `'a` 不代表生命周期等于 `'a`,而是大于等于 `'a`)。
|
|
|
|
|
|
|
|
|
|
|
|
回忆下“鲁迅”说的话,再参考上面的内容,可以得出:**在通过函数签名指定生命周期参数时,我们并没有改变传入引用或者返回引用的真实生命周期,而是告诉编译器当不满足此约束条件时,就拒绝编译通过**。
|
|
|
|
回忆下“鲁迅”说的话,再参考上面的内容,可以得出:**在通过函数签名指定生命周期参数时,我们并没有改变传入引用或者返回引用的真实生命周期,而是告诉编译器当不满足此约束条件时,就拒绝编译通过**。
|
|
|
|
|
|
|
|
|
|
|
@ -245,7 +245,7 @@ error[E0597]: `string2` does not live long enough
|
|
|
|
|
|
|
|
|
|
|
|
#### 深入思考生命周期标注
|
|
|
|
#### 深入思考生命周期标注
|
|
|
|
|
|
|
|
|
|
|
|
使用生命周期的方式往往取决于函数的功能,例如之前的 `longest` 函数,如果它永远只返回第一个参数 `x`,生命周期的标注该如何修改(该例子就是上面的小练习结果之一)?
|
|
|
|
使用生命周期的方式往往取决于函数的功能,例如之前的 `longest` 函数,如果它永远只返回第一个参数 `x`,生命周期的标注该如何修改(该例子就是上面的小练习结果之一)?
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
|
|
|
|
fn longest<'a>(x: &'a str, y: &str) -> &'a str {
|
|
|
@ -408,7 +408,7 @@ fn first_word<'a>(s: &'a str) -> &'a str {
|
|
|
|
|
|
|
|
|
|
|
|
例如一个引用参数的函数就有一个生命周期标注: `fn foo<'a>(x: &'a i32)`,两个引用参数的有两个生命周期标注:`fn foo<'a, 'b>(x: &'a i32, y: &'b i32)`, 依此类推。
|
|
|
|
例如一个引用参数的函数就有一个生命周期标注: `fn foo<'a>(x: &'a i32)`,两个引用参数的有两个生命周期标注:`fn foo<'a, 'b>(x: &'a i32, y: &'b i32)`, 依此类推。
|
|
|
|
|
|
|
|
|
|
|
|
2. **若只有一个输入生命周期(函数参数中只有一个引用类型),那么该生命周期会被赋给所有的输出生命周期**,也就是所有返回值的生命周期都等于该输入生命周期
|
|
|
|
2. **若只有一个输入生命周期(函数参数中只有一个引用类型),那么该生命周期会被赋给所有的输出生命周期**,也就是所有返回值的生命周期都等于该输入生命周期
|
|
|
|
|
|
|
|
|
|
|
|
例如函数 `fn foo(x: &i32) -> &i32`,`x` 参数的生命周期会被自动赋给返回值 `&i32`,因此该函数等同于 `fn foo<'a>(x: &'a i32) -> &'a i32`
|
|
|
|
例如函数 `fn foo(x: &i32) -> &i32`,`x` 参数的生命周期会被自动赋给返回值 `&i32`,因此该函数等同于 `fn foo<'a>(x: &'a i32) -> &'a i32`
|
|
|
|
|
|
|
|
|
|
|
|