From 8cdd7e9d6021eab9dfcfc12c2c59cd308ca09403 Mon Sep 17 00:00:00 2001 From: KaiserY Date: Mon, 24 Jul 2017 09:28:02 +0800 Subject: [PATCH] update ch19-02 --- src/ch19-02-advanced-lifetimes.md | 76 +++++++++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index de38e17..2123ec8 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -116,3 +116,79 @@ body at 15:55... 问题是 `parse_context` 函数返回 `parse` 返回值,所以 `parse_context` 返回值的生命周期也与 `Parser` 的生命周期相联系。不过 `parse_context` 函数中创建的 `Parser` 实例并不能存活到函数结束之后(它是临时的),同时 `context` 将会在函数的结尾离开作用域(`parse_context` 获取了它的所有权)。 +不允许一个在函数结尾离开作用域的值的引用。Rust 认为这是我们想要做的,因为我们将所有生命周期用相同的生命周期参数标记。这告诉了 Rust `Context` 中存放的字符串 slice 的生命周期与 `Parser` 中存放的 `Context` 引用的生命周期一致。 + +`parse_context` 函数并不知道 `parse` 函数里面是什么,返回的字符串 slice 将比 `Context` 和 `Parser` 都存活的更久,因此 `parse_context` 返回的引用指向字符串 slice,而不是 `Context` 或 `Parser`。 + +通过了解 `parse` 实现所做的工作,可以知道 `parse` 的返回值(的生命周期)与 `Parser` 相联系的唯一理由是它引用了 `Parser` 的 `Context`,也就是引用了这个字符串 slice,这正是 `parse_context` 所需要关心的生命周期。需要一个方法来告诉 Rust `Context` 中的字符串 slice 与 `Parser` 中 `Context` 的引用有着不同的生命周期,而且 `parse_context` 返回值与 `Context` 中字符串 slice 的生命周期相联系。 + +我们只能尝试像列表 19-15 那样给予 `Parser` 和 `Context` 不同的生命周期参数。这里选择了生命周期参数名 `'s` 和 `'c` 是为了使得 `Context` 中字符串 slice 与 `Parser` 中 `Context` 引用的生命周期显得更明了(英文首字母)。注意这并不能完全解决问题,不过这是一个开始,我们将看看为什么这还不足以能够编译代码。 + +```rust +struct Context<'s>(&'s str); + +struct Parser<'c, 's> { + context: &'c Context<'s>, +} + +impl<'c, 's> Parser<'c, 's> { + fn parse(&self) -> Result<(), &'s str> { + Err(&self.context.0[1..]) + } +} + +fn parse_context(context: Context) -> Result<(), &str> { + Parser { context: &context }.parse() +} +``` + +列表 19-15:为字符串 slice 和 `Context` 的引用指定不同的生命周期参数 + +这里在与列表 19-13 完全相同的地方标注了引用的生命周期,不过根据引用是字符串 slice 或 `Context` 与否使用了不同的参数。另外还在 `parse` 返回值的字符串 slice 部分增加了注解来表明它与 `Context` 中字符串 slice 的生命周期相关联。 + +这里是现在得到的错误: + +``` +error[E0491]: in type `&'c Context<'s>`, reference has a longer lifetime than the data it references + --> src/main.rs:4:5 + | +4 | context: &'c Context<'s>, + | ^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: the pointer is valid for the lifetime 'c as defined on the struct at 3:0 + --> src/main.rs:3:1 + | +3 | / struct Parser<'c, 's> { +4 | | context: &'c Context<'s>, +5 | | } + | |_^ +note: but the referenced data is only valid for the lifetime 's as defined on the struct at 3:0 + --> src/main.rs:3:1 + | +3 | / struct Parser<'c, 's> { +4 | | context: &'c Context<'s>, +5 | | } + | |_^ +``` + +Rust 并不知道 `'c` 与 `'s` 之间的任何联系。为了保证有效性,`Context`中引用的带有生命周期 `'s` 的数据需要遵守它比带有生命周期 `'c` 的 `Context` 的引用存活得更久的保证。如果 `'s` 不比 `'c` 更长久,那么 `Context` 的引用可能不再有效。 + +这就引出了本部分的要点:Rust 有一个叫做**生命周期子类型**的功能,这是一个指定一个生命周期不会短于另一个的方法。在声明生命周期参数的尖括号中,可以照常声明一个生命周期 `'a`,并通过语法 `'b: 'a` 声明一个不短于 `'a` 的生命周期 `'b`。 + +在 `Parser` 的定义中,为了表明 `'s`(字符串 slice 的生命周期)保证至少与 `'c`(`Context` 引用的生命周期)一样长,需将生命周期声明改为如此: + +```rust +# struct Context<'a>(&'a str); +# +struct Parser<'c, 's: 'c> { + context: &'c Context<'s>, +} +``` + +现在 `Parser` 中 `Context` 的引用与 `Context` 中字符串 slice 就有了不同的生命周期,并且保证了字符串 slice 的生命周期比 `Context` 引用的要长。 + +这是一个非常冗长的例子,不过正如本章的开头所提到的,这类功能是很小众的。你并不会经常需要这个语法,不过当出现类似这样的情形时,却还是有地方可以参考的。 + +### 生命周期 bound + +在第十章,我们讨论了如何在泛型类型上使用 trait bound。也可以像泛型那样为生命周期参数增加限制,这被称为**生命周期 bound**。例如,考虑一下一个封装了引用的类型。 \ No newline at end of file