From 619d09edea801fc3699cc45b7f0c02aeb2135b18 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Thu, 6 Jul 2017 23:16:27 +0800 Subject: [PATCH 01/23] Create ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 404 ++++++++++++++++++++++++++++++ 1 file changed, 404 insertions(+) create mode 100644 src/ch19-02-advanced-lifetimes.md diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md new file mode 100644 index 0000000..5ca16d5 --- /dev/null +++ b/src/ch19-02-advanced-lifetimes.md @@ -0,0 +1,404 @@ +## Advanced Lifetimes + +Back in Chapter 10, we learned how to annotate references with lifetime +parameters to help Rust understand how the lifetimes of different references +relate. We saw how most of the time, Rust will let you elide lifetimes, but +every reference has a lifetime. There are three advanced features of lifetimes +that we haven't covered though: *lifetime subtyping*, *lifetime +bounds*, and *trait object lifetimes*. + +### Lifetime Subtyping + +Imagine that we want to write a parser. To do this, we'll have a structure that +holds a reference to the string that we're parsing, and we'll call that struct +`Context`. We'll write a parser that will parse this string and return success +or failure. The parser will need to borrow the context to do the parsing. +Implementing this would look like the code in Listing 19-12, which won't +compile because we've left off the lifetime annotations for now: + +```rust,ignore +struct Context(&str); + +struct Parser { + context: &Context, +} + +impl Parser { + fn parse(&self) -> Result<(), &str> { + Err(&self.context.0[1..]) + } +} +``` + +Listing 19-12: Defining a `Context` struct that holds a +string slice, a `Parser` struct that holds a reference to a `Context` instance, +and a `parse` method that always returns an error referencing the string +slice + +For simplicity's sake, our `parse` function returns a `Result<(), &str>`. That +is, we don't do anything on success, and on failure we return the part of the +string slice that didn't parse correctly. A real implementation would have more +error information than that, and would actually return something created when +parsing succeeds, but we're leaving those parts of the implementation off since +they aren't relevant to the lifetimes part of this example. We're also defining +`parse` to always produce an error after the first byte. Note that this may +panic if the first byte is not on a valid character boundary; again, we're +simplifying the example in order to concentrate on the lifetimes involved. + +So how do we fill in the lifetime parameters for the string slice in `Context` +and the reference to the `Context` in `Parser`? The most straightforward thing +to do is to use the same lifetime everywhere, as shown in Listing 19-13: + +```rust +struct Context<'a>(&'a str); + +struct Parser<'a> { + context: &'a Context<'a>, +} + +impl<'a> Parser<'a> { + fn parse(&self) -> Result<(), &str> { + Err(&self.context.0[1..]) + } +} +``` + +Listing 19-13: Annotating all references in `Context` and +`Parser` with the same lifetime parameter + +This compiles fine. Next, in Listing 19-14, let's write a function that takes +an instance of `Context`, uses a `Parser` to parse that context, and returns +what `parse` returns. This won't quite work: + +```rust,ignore +fn parse_context(context: Context) -> Result<(), &str> { + Parser { context: &context }.parse() +} +``` + +Listing 19-14: An attempt to add a `parse_context` +function that takes a `Context` and uses a `Parser` + +We get two quite verbose errors when we try to compile the code with the +addition of the `parse_context` function: + +```text +error: borrowed value does not live long enough + --> :16:5 + | +16 | Parser { context: &context }.parse() + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough +17 | } + | - temporary value only lives until here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the +body at 15:55... + --> :15:56 + | +15 | fn parse_context(context: Context) -> Result<(), &str> { + | ________________________________________________________^ +16 | | Parser { context: &context }.parse() +17 | | } + | |_^ + +error: `context` does not live long enough + --> :16:24 + | +16 | Parser { context: &context }.parse() + | ^^^^^^^ does not live long enough +17 | } + | - borrowed value only lives until here + | +note: borrowed value must be valid for the anonymous lifetime #1 defined on the +body at 15:55... + --> :15:56 + | +15 | fn parse_context(context: Context) -> Result<(), &str> { + | ________________________________________________________^ +16 | | Parser { context: &context }.parse() +17 | | } + | |_^ +``` + +These errors are saying that both the `Parser` instance we're creating and the +`context` parameter live from the line that the `Parser` is created until the +end of the `parse_context` function, but they both need to live for the entire +lifetime of the function. + +In other words, `Parser` and `context` need to *outlive* the entire function +and be valid before the function starts as well as after it ends in order for +all the references in this code to always be valid. Both the `Parser` we're +creating and the `context` parameter go out of scope at the end of the +function, though (since `parse_context` takes ownership of `context`). + +Let's look at the definitions in Listing 19-13 again, especially the signature +of the `parse` method: + +```rust,ignore + fn parse(&self) -> Result<(), &str> { +``` + +Remember the elision rules? If we annotate the lifetimes of the references, the +signature would be: + +```rust,ignore + fn parse<'a>(&'a self) -> Result<(), &'a str> { +``` + +That is, the error part of the return value of `parse` has a lifetime that is +tied to the `Parser` instance's lifetime (that of `&self` in the `parse` method +signature). That makes sense, as the returned string slice references the +string slice in the `Context` instance that the `Parser` holds, and we've +specified in the definition of the `Parser` struct that the lifetime of the +reference to `Context` that `Parser` holds and the lifetime of the string slice +that `Context` holds should be the same. + +The problem is that the `parse_context` function returns the value returned +from `parse`, so the lifetime of the return value of `parse_context` is tied to +the lifetime of the `Parser` as well. But the `Parser` instance created in the +`parse_context` function won't live past the end of the function (it's +temporary), and the `context` will go out of scope at the end of the function +(`parse_context` takes ownership of it). + +We're not allowed to return a reference to a value that goes out of scope at +the end of the function. Rust thinks that's what we're trying to do because we +annotated all the lifetimes with the same lifetime parameter. That told Rust +the lifetime of the string slice that `Context` holds is the same as that of +the lifetime of the reference to `Context` that `Parser` holds. + +The `parse_context` function can't see that within the `parse` function, the +string slice returned will outlive both `Context` and `Parser`, and that the +reference `parse_context` returns refers to the string slice, not to `Context` +or `Parser`. + +By knowing what the implementation of `parse` does, we know that the only +reason that the return value of `parse` is tied to the `Parser` is because it's +referencing the `Parser`'s `Context`, which is referencing the string slice, so +it's really the lifetime of the string slice that `parse_context` needs to care +about. We need a way to tell Rust that the string slice in `Context` and the +reference to the `Context` in `Parser` have different lifetimes and that the +return value of `parse_context` is tied to the lifetime of the string slice in +`Context`. + +We could try only giving `Parser` and `Context` different lifetime parameters +as shown in Listing 19-15. We've chosen the lifetime parameter names `'s` and +`'c` here to be clearer about which lifetime goes with the string slice in +`Context` and which goes with the reference to `Context` in `Parser`. Note that +this won't completely fix the problem, but it's a start and we'll look at why +this isn't sufficient when we try to compile. + +```rust,ignore +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() +} +``` + +Listing 19-15: Specifying different lifetime parameters +for the references to the string slice and to `Context` + +We've annotated the lifetimes of the references in all the same places that we +annotated them in Listing 19-13, but used different parameters depending on +whether the reference goes with the string slice or with `Context`. We've also +added an annotation to the string slice part of the return value of `parse` to +indicate that it goes with the lifetime of the string slice in `Context`. + +Here's the error we get now: + +```text +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 doesn't know of any relationship between `'c` and `'s`. In order to be +valid, the referenced data in `Context` with lifetime `'s` needs to be +constrained to guarantee that it lives longer than the reference to `Context` +that has lifetime `'c`. If `'s` is not longer than `'c`, then the reference to +`Context` might not be valid. + +Which gets us to the point of this section: Rust has a feature called *lifetime +subtyping*, which is a way to specify that one lifetime parameter lives at +least as long as another one. In the angle brackets where we declare lifetime +parameters, we can declare a lifetime `'a` as usual, and declare a lifetime +`'b` that lives at least as long as `'a` by declaring `'b` with the syntax `'b: +'a`. + +In our definition of `Parser`, in order to say that `'s` (the lifetime of the +string slice) is guaranteed to live at least as long as `'c` (the lifetime of +the reference to `Context`), we change the lifetime declarations to look like +this: + +```rust +# struct Context<'a>(&'a str); +# +struct Parser<'c, 's: 'c> { + context: &'c Context<'s>, +} +``` + +Now, the reference to `Context` in the `Parser` and the reference to the string +slice in the `Context` have different lifetimes, and we've ensured that the +lifetime of the string slice is longer than the reference to the `Context`. + +That was a very long-winded example, but as we mentioned at the start of this +chapter, these features are pretty niche. You won't often need this syntax, but +it can come up in situations like this one, where you need to refer to +something you have a reference to. + +### Lifetime Bounds + +In Chapter 10, we discussed how to use trait bounds on generic types. We can +also add lifetime parameters as constraints on generic types. For example, +let's say we wanted to make a wrapper over references. Remember `RefCell` +from Chapter 15? This is how the `borrow` and `borrow_mut` methods work; they +return wrappers over references in order to keep track of the borrowing rules +at runtime. The struct definition, without lifetime parameters for now, would +look like Listing 19-16: + +```rust,ignore +struct Ref(&T); +``` + +Listing 19-16: Defining a struct to wrap a reference to a +generic type; without lifetime parameters to start + +However, using no lifetime bounds at all gives an error because Rust doesn't +know how long the generic type `T` will live: + +```text +error[E0309]: the parameter type `T` may not live long enough + --> :2:19 + | +2 | struct Ref<'a, T>(&'a T); + | ^^^^^^ + | + = help: consider adding an explicit lifetime bound `T: 'a`... +note: ...so that the reference type `&'a T` does not outlive the data it points at + --> :2:19 + | +2 | struct Ref<'a, T>(&'a T); + | ^^^^^^ +``` + +This is the same error that we'd get if we filled in `T` with a concrete type, +like `struct Ref(&i32)`; all references in struct definitions need a lifetime +parameter. However, because we have a generic type parameter, we can't add a +lifetime parameter in the same way. Defining `Ref` as `struct Ref<'a>(&'a T)` +will result in an error because Rust can't determine that `T` lives long +enough. Since `T` can be any type, `T` could itself be a reference or it could +be a type that holds one or more references, each of which have their own +lifetimes. + +Rust helpfully gave us good advice on how to specify the lifetime parameter in +this case: + +```text +consider adding an explicit lifetime bound `T: 'a` so that the reference type +`&'a T` does not outlive the data it points to. +``` + +The code in Listing 19-17 works because `T: 'a` syntax specifies that `T` can +be any type, but if it contains any references, `T` must live as long as `'a`: + +```rust +struct Ref<'a, T: 'a>(&'a T); +``` + +Listing 19-17: Adding lifetime bounds on `T` to specify +that any references in `T` live at least as long as `'a` + +We could choose to solve this in a different way as shown in Listing 19-18 by +bounding `T` on `'static`. This means if `T` contains any references, they must +have the `'static` lifetime: + +```rust +struct StaticRef(&'static T); +``` + +Listing 19-18: Adding a `'static` lifetime bound to `T` +to constrain `T` to types that have only `'static` references or no +references + +Types with no references count as `T: 'static`. Because `'static` means the +reference must live as long as the entire program, a type that contains no +references meets the criteria of all references living as long as the entire +program (since there are no references). Think of it this way: if the borrow +checker is concerned about references living long enough, then there's no real +distinction between a type that has no references and a type that has +references that live forever; both of them are the same for the purpose of +determining whether or not a reference has a shorter lifetime than what it +refers to. + +### Trait Object Lifetimes + +In Chapter 17, we learned about trait objects that consist of putting a trait +behind a reference in order to use dynamic dispatch. However, we didn't discuss +what happens if the type implementing the trait used in the trait object has a +lifetime. Consider Listing 19-19, where we have a trait `Foo` and a struct +`Bar` that holds a reference (and thus has a lifetime parameter) that +implements trait `Foo`, and we want to use an instance of `Bar` as the trait +object `Box`: + +```rust +trait Foo { } + +struct Bar<'a> { + x: &'a i32, +} + +impl<'a> Foo for Bar<'a> { } + +let num = 5; + +let obj = Box::new(Bar { x: &num }) as Box; +``` + +Listing 19-19: Using a type that has a lifetime parameter +with a trait object + +This code compiles without any errors, even though we haven't said anything +about the lifetimes involved in `obj`. This works because there are rules +having to do with lifetimes and trait objects: + +* The default lifetime of a trait object is `'static`. +* If we have `&'a X` or `&'a mut X`, then the default is `'a`. +* If we have a single `T: 'a` clause, then the default is `'a`. +* If we have multiple `T: 'a`-like clauses, then there is no default; we must + be explicit. + +When we must be explicit, we can add a lifetime bound on a trait object like +`Box` with the syntax `Box` or `Box`, depending +on what's needed. Just as with the other bounds, this means that any +implementer of the `Foo` trait that has any references inside must have the +lifetime specified in the trait object bounds as those references. + +Next, let's take a look at some other advanced features dealing with traits! From 74d72513469758ed8209aa833abc900ed914ab94 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Thu, 6 Jul 2017 23:18:42 +0800 Subject: [PATCH 02/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 5ca16d5..431904a 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -1,13 +1,13 @@ -## Advanced Lifetimes +## 高级生命周期 -Back in Chapter 10, we learned how to annotate references with lifetime +回到第10章, we learned how to annotate references with lifetime parameters to help Rust understand how the lifetimes of different references relate. We saw how most of the time, Rust will let you elide lifetimes, but every reference has a lifetime. There are three advanced features of lifetimes that we haven't covered though: *lifetime subtyping*, *lifetime bounds*, and *trait object lifetimes*. -### Lifetime Subtyping +### 生命周期子类型 Imagine that we want to write a parser. To do this, we'll have a structure that holds a reference to the string that we're parsing, and we'll call that struct From 452a08c6c020db352cf6d558d921c8ace6adeef6 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Mon, 10 Jul 2017 22:35:58 +0800 Subject: [PATCH 03/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 431904a..d438dd0 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -1,20 +1,11 @@ ## 高级生命周期 -回到第10章, we learned how to annotate references with lifetime -parameters to help Rust understand how the lifetimes of different references -relate. We saw how most of the time, Rust will let you elide lifetimes, but -every reference has a lifetime. There are three advanced features of lifetimes -that we haven't covered though: *lifetime subtyping*, *lifetime -bounds*, and *trait object lifetimes*. +回到第10章, 我们学习了如何用生命周期注解引用参数来帮助 Rust 理解不同的引用所关联的生命周期. 我们看到大多数时候, Rust 都会让你忽略生命周期, 但是每个引用都有一个生命周期. 还有三个关于生命周期的高级特性我们以前没有介绍, 它们是: *生命周期子类型(lifetime subtyping)*, *生命周期绑定(lifetime +bounds)*, 和 *trait 对象生命周期*. ### 生命周期子类型 -Imagine that we want to write a parser. To do this, we'll have a structure that -holds a reference to the string that we're parsing, and we'll call that struct -`Context`. We'll write a parser that will parse this string and return success -or failure. The parser will need to borrow the context to do the parsing. -Implementing this would look like the code in Listing 19-12, which won't -compile because we've left off the lifetime annotations for now: +想象一下我们想写一个解释器. 为此, 我们将有一个持有我们将解析的字符串的引用的结构, 我们把这个结构叫做`Context`. 我们将写一个能够解析这个字符串并返回成功或失败的解析器. 该解析器需要借用这个上下文来完成解析. 实现这个功能的代码如例 19-12, 但是这个代码不能被编译因为我们没有使用生命周期注解: ```rust,ignore struct Context(&str); From fa2d884e691df9a51b829b42ff3106a506e7ed81 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Mon, 10 Jul 2017 22:42:01 +0800 Subject: [PATCH 04/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index d438dd0..b79dc6b 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -5,7 +5,7 @@ bounds)*, 和 *trait 对象生命周期*. ### 生命周期子类型 -想象一下我们想写一个解释器. 为此, 我们将有一个持有我们将解析的字符串的引用的结构, 我们把这个结构叫做`Context`. 我们将写一个能够解析这个字符串并返回成功或失败的解析器. 该解析器需要借用这个上下文来完成解析. 实现这个功能的代码如例 19-12, 但是这个代码不能被编译因为我们没有使用生命周期注解: +想象一下我们想写一个解释器. 为此, 我们将有一个持有我们将解析的字符串的引用的结构, 我们把这个结构叫做`Context`. 我们将写一个能够解析这个字符串并返回成功或失败的解析器. 该解析器需要借用这个上下文(解析器中的`context`属性)来完成解析. 实现这个功能的代码如例 19-12, 但是这个代码不能被编译因为我们没有使用生命周期注解: ```rust,ignore struct Context(&str); From 9d9b8daff8604879705483d4ca9c585ab3d89c2e Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Tue, 11 Jul 2017 17:58:57 +0800 Subject: [PATCH 05/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index b79dc6b..a660461 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -5,7 +5,7 @@ bounds)*, 和 *trait 对象生命周期*. ### 生命周期子类型 -想象一下我们想写一个解释器. 为此, 我们将有一个持有我们将解析的字符串的引用的结构, 我们把这个结构叫做`Context`. 我们将写一个能够解析这个字符串并返回成功或失败的解析器. 该解析器需要借用这个上下文(解析器中的`context`属性)来完成解析. 实现这个功能的代码如例 19-12, 但是这个代码不能被编译因为我们没有使用生命周期注解: +想象一下我们想写一个解释器. 为此, 我们需要一个持有即将被解析的字符串的引用的结构, 我们把这个结构叫做`Context`. 我们将写一个能够解析这个字符串并返回成功或失败的解析器. 该解析器需要借用这个上下文(解析器中的`context`属性)来完成解析. 实现这个功能的代码如例 19-12, 但是这个代码不能被编译因为我们没有使用生命周期注解: ```rust,ignore struct Context(&str); @@ -21,17 +21,9 @@ impl Parser { } ``` -Listing 19-12: Defining a `Context` struct that holds a -string slice, a `Parser` struct that holds a reference to a `Context` instance, -and a `parse` method that always returns an error referencing the string -slice +例19-12: 定义持有一个字符串切片的`Context`结构, 一个持有某个`Context`实例引用的`Parser`结构, 和一个总是返回一个错误的`parse`方法, 这个被返回的错误引用了该字符串切片 -For simplicity's sake, our `parse` function returns a `Result<(), &str>`. That -is, we don't do anything on success, and on failure we return the part of the -string slice that didn't parse correctly. A real implementation would have more -error information than that, and would actually return something created when -parsing succeeds, but we're leaving those parts of the implementation off since -they aren't relevant to the lifetimes part of this example. We're also defining +为了简单起见, 我们的`parse`函数返回一个`Result<(), &str>`. 也就是说, 我们在成功时不做任何事情, 在失败时我们返回部分没有解析正确的字符串切片. 一个真正的实现将会有更多的错误信息, 而且实际上在解析成功时会返回当时创建的内容, 但是我们将实现的这部分省略了因为它们与本例的生命周期无关. We're also defining `parse` to always produce an error after the first byte. Note that this may panic if the first byte is not on a valid character boundary; again, we're simplifying the example in order to concentrate on the lifetimes involved. From 8a88048582eec8926a105bbb69e3000c9318a8a5 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Tue, 11 Jul 2017 22:40:25 +0800 Subject: [PATCH 06/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index a660461..20eb761 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -23,14 +23,9 @@ impl Parser { 例19-12: 定义持有一个字符串切片的`Context`结构, 一个持有某个`Context`实例引用的`Parser`结构, 和一个总是返回一个错误的`parse`方法, 这个被返回的错误引用了该字符串切片 -为了简单起见, 我们的`parse`函数返回一个`Result<(), &str>`. 也就是说, 我们在成功时不做任何事情, 在失败时我们返回部分没有解析正确的字符串切片. 一个真正的实现将会有更多的错误信息, 而且实际上在解析成功时会返回当时创建的内容, 但是我们将实现的这部分省略了因为它们与本例的生命周期无关. We're also defining -`parse` to always produce an error after the first byte. Note that this may -panic if the first byte is not on a valid character boundary; again, we're -simplifying the example in order to concentrate on the lifetimes involved. +为了简单起见, 我们的`parse`函数返回一个`Result<(), &str>`. 也就是说, 我们在成功时不做任何事情, 在失败时我们返回部分没有解析正确的字符串切片. 一个真正的实现将会有更多的错误信息, 而且实际上在解析成功时会返回当时创建的内容, 但是我们将实现的这部分省略了因为它们与本例的生命周期无关. 我们也定义`parse`总在第一个字节后产生一个错误. 请注意如果第一个字节不在有效的字符边界内这可能会出现错误; 再说一下, 为了把注意力放在生命周期上, 我们简化了这个例子. -So how do we fill in the lifetime parameters for the string slice in `Context` -and the reference to the `Context` in `Parser`? The most straightforward thing -to do is to use the same lifetime everywhere, as shown in Listing 19-13: +那么我们如何设置`Context`中的字符串切片的生命周期参数和`Parser`中的`Context`引用呢? 最直接的办法就是使用同样的生命周期, 如例19-13所示: ```rust struct Context<'a>(&'a str); @@ -46,12 +41,9 @@ impl<'a> Parser<'a> { } ``` -Listing 19-13: Annotating all references in `Context` and -`Parser` with the same lifetime parameter +例19-13: 限定`Context`和`Parser`中的所有引用具有同样的生命周期参数 -This compiles fine. Next, in Listing 19-14, let's write a function that takes -an instance of `Context`, uses a `Parser` to parse that context, and returns -what `parse` returns. This won't quite work: +这样就能够编译了. 然后, 在例19-14中, 让我们写一个以`Context`实例为参数的函数, 该函数用一个`Parser`来解析那个`Context`实例并把`parse`方法的结果直接返回. 但是这个代码不能正常工作: ```rust,ignore fn parse_context(context: Context) -> Result<(), &str> { @@ -59,11 +51,9 @@ fn parse_context(context: Context) -> Result<(), &str> { } ``` -Listing 19-14: An attempt to add a `parse_context` -function that takes a `Context` and uses a `Parser` +例19-14: 尝试添加有一个`parse_context`函数, 该函数有一个`Context`参数, 在函数中使用了`Parser` -We get two quite verbose errors when we try to compile the code with the -addition of the `parse_context` function: +当我们试图用新添加的`parse_context`函数来编译代码时我们会得到两个相当详细的错误: ```text error: borrowed value does not live long enough From 6f8df93589a1e23851998d2aa1cc2f8fb5a08f95 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Wed, 12 Jul 2017 10:04:55 +0800 Subject: [PATCH 07/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 20eb761..0ec6b65 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -93,25 +93,17 @@ body at 15:55... | |_^ ``` -These errors are saying that both the `Parser` instance we're creating and the -`context` parameter live from the line that the `Parser` is created until the -end of the `parse_context` function, but they both need to live for the entire -lifetime of the function. +这些错误表明不管是我们创建的`Parser`实例还是作用于从`Parser`被创建的行开始到`parse_context`函数结束的`context`参数, 都需要拥有整个函数的生命周期. -In other words, `Parser` and `context` need to *outlive* the entire function -and be valid before the function starts as well as after it ends in order for -all the references in this code to always be valid. Both the `Parser` we're -creating and the `context` parameter go out of scope at the end of the -function, though (since `parse_context` takes ownership of `context`). +换句话说, `Parser`和`context`存活的时间要比整个函数更长, 为了让代码中的所有引用都有效它们也应该在函数被调用前后都有效. 不管是我们正创建的`Parser`还是在函数结束时就会结束作用域的`context`参数都是如此(因为`parse_context`函数会获得`context`的所有权). -Let's look at the definitions in Listing 19-13 again, especially the signature -of the `parse` method: +让我们再看一下例19-13中的定义, 特别是`parse`方法的声明: ```rust,ignore fn parse(&self) -> Result<(), &str> { ``` -Remember the elision rules? If we annotate the lifetimes of the references, the +还记得生命周期的省略规则吗? If we annotate the lifetimes of the references, the signature would be: ```rust,ignore From 8073e9b9c860904cce3dda523dd0e5a00dd27928 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Wed, 12 Jul 2017 22:32:49 +0800 Subject: [PATCH 08/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 24 ++++-------------------- 1 file changed, 4 insertions(+), 20 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 0ec6b65..efd2df5 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -103,33 +103,17 @@ body at 15:55... fn parse(&self) -> Result<(), &str> { ``` -还记得生命周期的省略规则吗? If we annotate the lifetimes of the references, the -signature would be: +还记得生命周期的省略规则吗? 如果我们注解引用的生命周期, 那么可以这样写: ```rust,ignore fn parse<'a>(&'a self) -> Result<(), &'a str> { ``` -That is, the error part of the return value of `parse` has a lifetime that is -tied to the `Parser` instance's lifetime (that of `&self` in the `parse` method -signature). That makes sense, as the returned string slice references the -string slice in the `Context` instance that the `Parser` holds, and we've -specified in the definition of the `Parser` struct that the lifetime of the -reference to `Context` that `Parser` holds and the lifetime of the string slice -that `Context` holds should be the same. +也就是说, `parse`函数返回值的错误中的部分是因为它有一个生命周期被绑定到了`Parser`实例的生命周期上面(也就是`parse`方法中的`&self`). 这就是了, 因为被返回的字符串切片引用了`Parser`持有的`Context`实例中的字符串切片, 并且我们已经在`Parser`结构的定义中指定了`Parser`中的`Context`的引用的生命周期和`Context`持有的字符串切片的生命周期是一样的. -The problem is that the `parse_context` function returns the value returned -from `parse`, so the lifetime of the return value of `parse_context` is tied to -the lifetime of the `Parser` as well. But the `Parser` instance created in the -`parse_context` function won't live past the end of the function (it's -temporary), and the `context` will go out of scope at the end of the function -(`parse_context` takes ownership of it). +问题是`parse_context`函数要返回`parse`方法的返回值, 这样`parse_context`的返回值的生命周期也就被绑定到了`Parser`的生命周期上了. 但是在`parse_context`函数中创建的`Parser`实例在函数结束后就不会存活了(它是临时的), 并且`context`在函数结束后也会越过作用域(`parse_context`拥有它的所有权). -We're not allowed to return a reference to a value that goes out of scope at -the end of the function. Rust thinks that's what we're trying to do because we -annotated all the lifetimes with the same lifetime parameter. That told Rust -the lifetime of the string slice that `Context` holds is the same as that of -the lifetime of the reference to `Context` that `Parser` holds. +我们不能返回只在函数作用域内才能存活的值的引用. Rust就认为我们在做这个事情, 因为我们把所有的生命周期参数都注解成一样的了. 这就告诉 Rust `Context`持有的字符串切片的生命周期和`Parser`持有的`Context`引用的生命周期是一样的. The `parse_context` function can't see that within the `parse` function, the string slice returned will outlive both `Context` and `Parser`, and that the From 9ddf0fc7c9c785361e235efcd18922b04f203de8 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Thu, 13 Jul 2017 23:20:33 +0800 Subject: [PATCH 09/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index efd2df5..2b2a012 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -115,16 +115,9 @@ body at 15:55... 我们不能返回只在函数作用域内才能存活的值的引用. Rust就认为我们在做这个事情, 因为我们把所有的生命周期参数都注解成一样的了. 这就告诉 Rust `Context`持有的字符串切片的生命周期和`Parser`持有的`Context`引用的生命周期是一样的. -The `parse_context` function can't see that within the `parse` function, the -string slice returned will outlive both `Context` and `Parser`, and that the -reference `parse_context` returns refers to the string slice, not to `Context` -or `Parser`. +`parse_context`函数看不到`parse`函数中的东西, 被返回的字符串切片的存活时间将比`Context`和`Parser`更长, 并且`parse_context`返回的是字符串切片的引用而不是`Context`和`Parser`的引用. -By knowing what the implementation of `parse` does, we know that the only -reason that the return value of `parse` is tied to the `Parser` is because it's -referencing the `Parser`'s `Context`, which is referencing the string slice, so -it's really the lifetime of the string slice that `parse_context` needs to care -about. We need a way to tell Rust that the string slice in `Context` and the +通过了解`parse`的实现 does, 我们明白了`parse`的返回值被绑定到`Parser`的唯一原因是因为它引用了`Prser`中的`Context`, 也就是一个字符串切片的引用, 所以它的生命周期才是`parse_context`需要关心的字符串切片的真正的生命周期. We need a way to tell Rust that the string slice in `Context` and the reference to the `Context` in `Parser` have different lifetimes and that the return value of `parse_context` is tied to the lifetime of the string slice in `Context`. From e7e9cfa337aa003246b5ca4d7ed9b0539fba269d Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Mon, 17 Jul 2017 12:37:32 +0800 Subject: [PATCH 10/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 2b2a012..acc22c3 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -117,10 +117,7 @@ body at 15:55... `parse_context`函数看不到`parse`函数中的东西, 被返回的字符串切片的存活时间将比`Context`和`Parser`更长, 并且`parse_context`返回的是字符串切片的引用而不是`Context`和`Parser`的引用. -通过了解`parse`的实现 does, 我们明白了`parse`的返回值被绑定到`Parser`的唯一原因是因为它引用了`Prser`中的`Context`, 也就是一个字符串切片的引用, 所以它的生命周期才是`parse_context`需要关心的字符串切片的真正的生命周期. We need a way to tell Rust that the string slice in `Context` and the -reference to the `Context` in `Parser` have different lifetimes and that the -return value of `parse_context` is tied to the lifetime of the string slice in -`Context`. +通过了解`parse`的实现, 我们明白了`parse`的返回值被绑定到`Parser`的唯一原因是因为它引用了`Prser`中的`Context`, 也就是对一个字符串切片的引用, 所以它的生命周期才是`parse_context`需要关心的字符串切片的真正的生命周期. 我们需要告诉 Rust 在`Context`中的字符串切片和对`Parser`中的`Context`的引用有不同的生命周期, 我们还要告诉 Rust `parse_context` 函数的返回值被绑定到了`Context`中的字符串切片的生命周期. We could try only giving `Parser` and `Context` different lifetime parameters as shown in Listing 19-15. We've chosen the lifetime parameter names `'s` and From aced36ae90a7f6106181e1d216c37e5dfff4214a Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Tue, 18 Jul 2017 09:39:08 +0800 Subject: [PATCH 11/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index acc22c3..70f7011 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -119,12 +119,7 @@ body at 15:55... 通过了解`parse`的实现, 我们明白了`parse`的返回值被绑定到`Parser`的唯一原因是因为它引用了`Prser`中的`Context`, 也就是对一个字符串切片的引用, 所以它的生命周期才是`parse_context`需要关心的字符串切片的真正的生命周期. 我们需要告诉 Rust 在`Context`中的字符串切片和对`Parser`中的`Context`的引用有不同的生命周期, 我们还要告诉 Rust `parse_context` 函数的返回值被绑定到了`Context`中的字符串切片的生命周期. -We could try only giving `Parser` and `Context` different lifetime parameters -as shown in Listing 19-15. We've chosen the lifetime parameter names `'s` and -`'c` here to be clearer about which lifetime goes with the string slice in -`Context` and which goes with the reference to `Context` in `Parser`. Note that -this won't completely fix the problem, but it's a start and we'll look at why -this isn't sufficient when we try to compile. +我们可以尝试像例 19-15 中显示的那样只给`Parser`和`Context`不同的生命周期参数. 我们选择了生命周期参数`'s`和`'c`, 这样可以很方便的区分哪个生命周期伴随`Context`中的字符串切片, 哪个生命周期伴随`Parser`中的`Context`引用. 注意这还不能完全修正问题, 但这是一个开始, 编译时我们就会明白为什么它还不够. ```rust,ignore struct Context<'s>(&'s str); @@ -144,8 +139,7 @@ fn parse_context(context: Context) -> Result<(), &str> { } ``` -Listing 19-15: Specifying different lifetime parameters -for the references to the string slice and to `Context` +例19-15: 给对字符串切片和对`Context`的引用指定不同的生命周期参数 We've annotated the lifetimes of the references in all the same places that we annotated them in Listing 19-13, but used different parameters depending on From 4bb2d07e4524ae61e88b28c192efbcbc22c045b3 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Wed, 19 Jul 2017 10:40:46 +0800 Subject: [PATCH 12/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 70f7011..dcf7677 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -141,13 +141,9 @@ fn parse_context(context: Context) -> Result<(), &str> { 例19-15: 给对字符串切片和对`Context`的引用指定不同的生命周期参数 -We've annotated the lifetimes of the references in all the same places that we -annotated them in Listing 19-13, but used different parameters depending on -whether the reference goes with the string slice or with `Context`. We've also -added an annotation to the string slice part of the return value of `parse` to -indicate that it goes with the lifetime of the string slice in `Context`. +在例 19-13 中, 我们在所有相同的地方注释了引用的生命周期, 但是是否使用不同的参数却依赖于引用是否与字符串切片或`Context`一起使用. 我们也在`parse`的返回值的字符串切片部分添加了一个生命周期注解来表明它与`Context`的生命周期一样. -Here's the error we get now: +下面就是例19-15编译的结果: ```text error[E0491]: in type `&'c Context<'s>`, reference has a longer lifetime than the data it references @@ -172,11 +168,7 @@ note: but the referenced data is only valid for the lifetime 's as defined on th | |_^ ``` -Rust doesn't know of any relationship between `'c` and `'s`. In order to be -valid, the referenced data in `Context` with lifetime `'s` needs to be -constrained to guarantee that it lives longer than the reference to `Context` -that has lifetime `'c`. If `'s` is not longer than `'c`, then the reference to -`Context` might not be valid. +Rust 并不知道 `'c` 和 `'s` 之间的关系. `Context`中被引用的数据的生命周期是`'s`, 对`Context`的引用有生命周期`'c`, 为了让代码有效, 需要保证生命周期 `'s` 比生命周期 `'c` 更长. 如果 `'s` 存活的时间没有 `'c` 长, 那么对 `Context` 的引用可能就要出问题. Which gets us to the point of this section: Rust has a feature called *lifetime subtyping*, which is a way to specify that one lifetime parameter lives at From a6240987721d2b04a8ec61fe29a1e1cae7fe6ec3 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Sun, 23 Jul 2017 01:50:24 +0800 Subject: [PATCH 13/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index dcf7677..60f97ab 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -170,9 +170,7 @@ note: but the referenced data is only valid for the lifetime 's as defined on th Rust 并不知道 `'c` 和 `'s` 之间的关系. `Context`中被引用的数据的生命周期是`'s`, 对`Context`的引用有生命周期`'c`, 为了让代码有效, 需要保证生命周期 `'s` 比生命周期 `'c` 更长. 如果 `'s` 存活的时间没有 `'c` 长, 那么对 `Context` 的引用可能就要出问题. -Which gets us to the point of this section: Rust has a feature called *lifetime -subtyping*, which is a way to specify that one lifetime parameter lives at -least as long as another one. In the angle brackets where we declare lifetime +现在就让我们来看这一节: Rust 有一个被称为*生命周期子类型(lifetime subtyping)*的特性, 它可以指明一个生命周期参数的存活时间不会比另一个生命周期短. In the angle brackets where we declare lifetime parameters, we can declare a lifetime `'a` as usual, and declare a lifetime `'b` that lives at least as long as `'a` by declaring `'b` with the syntax `'b: 'a`. From 23d5b65a774f4b041ee27d66c669bdc6b948c152 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Sun, 23 Jul 2017 01:53:52 +0800 Subject: [PATCH 14/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 60f97ab..bf12e03 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -170,7 +170,7 @@ note: but the referenced data is only valid for the lifetime 's as defined on th Rust 并不知道 `'c` 和 `'s` 之间的关系. `Context`中被引用的数据的生命周期是`'s`, 对`Context`的引用有生命周期`'c`, 为了让代码有效, 需要保证生命周期 `'s` 比生命周期 `'c` 更长. 如果 `'s` 存活的时间没有 `'c` 长, 那么对 `Context` 的引用可能就要出问题. -现在就让我们来看这一节: Rust 有一个被称为*生命周期子类型(lifetime subtyping)*的特性, 它可以指明一个生命周期参数的存活时间不会比另一个生命周期短. In the angle brackets where we declare lifetime +本节就是为了解决这个问题: Rust 有一个被称为 *生命周期子类型 (lifetime subtyping)* 的特性, 它可以指明一个生命周期参数的存活时间不会比另一个生命周期短. In the angle brackets where we declare lifetime parameters, we can declare a lifetime `'a` as usual, and declare a lifetime `'b` that lives at least as long as `'a` by declaring `'b` with the syntax `'b: 'a`. From 784f8980c4279b5986199d9fe2c1eb7025fb38a6 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Thu, 27 Jul 2017 09:39:55 +0800 Subject: [PATCH 15/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index bf12e03..97b4e6b 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -170,15 +170,9 @@ note: but the referenced data is only valid for the lifetime 's as defined on th Rust 并不知道 `'c` 和 `'s` 之间的关系. `Context`中被引用的数据的生命周期是`'s`, 对`Context`的引用有生命周期`'c`, 为了让代码有效, 需要保证生命周期 `'s` 比生命周期 `'c` 更长. 如果 `'s` 存活的时间没有 `'c` 长, 那么对 `Context` 的引用可能就要出问题. -本节就是为了解决这个问题: Rust 有一个被称为 *生命周期子类型 (lifetime subtyping)* 的特性, 它可以指明一个生命周期参数的存活时间不会比另一个生命周期短. In the angle brackets where we declare lifetime -parameters, we can declare a lifetime `'a` as usual, and declare a lifetime -`'b` that lives at least as long as `'a` by declaring `'b` with the syntax `'b: -'a`. +本节就是为了解决这个问题: Rust 有一个被称为 *生命周期子类型 (lifetime subtyping)* 的特性, 它可以指明一个生命周期参数的存活时间不会比另一个生命周期短. 在我们声明生命周期的尖括号里, 我们可以声明一个普通的生命周期 `'a`, 还可以通过语法 `'b: 'a` 声明一个存活时间至少和 `'a` 一样长的生命周期 `'b`. -In our definition of `Parser`, in order to say that `'s` (the lifetime of the -string slice) is guaranteed to live at least as long as `'c` (the lifetime of -the reference to `Context`), we change the lifetime declarations to look like -this: +在我们的 `Parser` 的定义里, 为了保证 `'s`(字符串切片的生命周期) 的生命周期的存活时间至少和 `'c`(对 `Context` 引用的生命周期) 一样长, 我们把生命周期的声明改成这样: ```rust # struct Context<'a>(&'a str); From 537b3d41fd12b9315e3e31c9babf901d80efb1d7 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Fri, 28 Jul 2017 11:26:02 +0800 Subject: [PATCH 16/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 22 +++++----------------- 1 file changed, 5 insertions(+), 17 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 97b4e6b..b183ec6 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -182,31 +182,19 @@ struct Parser<'c, 's: 'c> { } ``` -Now, the reference to `Context` in the `Parser` and the reference to the string -slice in the `Context` have different lifetimes, and we've ensured that the -lifetime of the string slice is longer than the reference to the `Context`. +现在, 对`Parser`中的`Context`的引用和对`Context`中的字符串切片的引用就有了不同的生命周期, 并且我们还保证对字符串切片的引用的生命周期比对`Context`的引用的生命周期更长. -That was a very long-winded example, but as we mentioned at the start of this -chapter, these features are pretty niche. You won't often need this syntax, but -it can come up in situations like this one, where you need to refer to -something you have a reference to. +哦, 这个例子真的很长, 但正如本章开头所说, 这些功能非常适用. 你不会经常使用这个语法, 但是在你需要引用另一个引用中的某些内容的时候你就用得上它了. -### Lifetime Bounds +### 生命周期绑定 -In Chapter 10, we discussed how to use trait bounds on generic types. We can -also add lifetime parameters as constraints on generic types. For example, -let's say we wanted to make a wrapper over references. Remember `RefCell` -from Chapter 15? This is how the `borrow` and `borrow_mut` methods work; they -return wrappers over references in order to keep track of the borrowing rules -at runtime. The struct definition, without lifetime parameters for now, would -look like Listing 19-16: +我们在第10章里面讨论过如何在泛型上使用 trait 绑定. 我们也可以在泛型上添加生命周期参数来作为约束. 比如, 我们想在引用上做一个封装. 还记得第15章中的 `RefCell` 吗? 它就是 `borrow` 和 `borrow_mut` 方法的工作原理; 为了在运行时追踪借用规则它们返回引用的封装. 例 19-16 中给出了一个没有生命周期参数的结构的定义: ```rust,ignore struct Ref(&T); ``` -Listing 19-16: Defining a struct to wrap a reference to a -generic type; without lifetime parameters to start +例 19-16: 先不使用生命周期参数定义一个结构来封装一个对泛型的引用 However, using no lifetime bounds at all gives an error because Rust doesn't know how long the generic type `T` will live: From ddb86e3764f85ccd1a6cd5feb2407a66759d608d Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Wed, 2 Aug 2017 10:10:55 +0800 Subject: [PATCH 17/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 24 +++++++----------------- 1 file changed, 7 insertions(+), 17 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index b183ec6..a9467ea 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -1,6 +1,6 @@ ## 高级生命周期 -回到第10章, 我们学习了如何用生命周期注解引用参数来帮助 Rust 理解不同的引用所关联的生命周期. 我们看到大多数时候, Rust 都会让你忽略生命周期, 但是每个引用都有一个生命周期. 还有三个关于生命周期的高级特性我们以前没有介绍, 它们是: *生命周期子类型(lifetime subtyping)*, *生命周期绑定(lifetime +回顾一下第10章, 我们学习了如何用生命周期注解引用参数来帮助 Rust 理解不同的引用所关联的生命周期. 我们看到大多数时候, Rust 都会让你忽略生命周期, 但是每个引用都有一个生命周期. 还有三个关于生命周期的高级特性我们以前没有介绍, 它们是: *生命周期子类型(lifetime subtyping)*, *生命周期绑定(lifetime bounds)*, 和 *trait 对象生命周期*. ### 生命周期子类型 @@ -188,7 +188,7 @@ struct Parser<'c, 's: 'c> { ### 生命周期绑定 -我们在第10章里面讨论过如何在泛型上使用 trait 绑定. 我们也可以在泛型上添加生命周期参数来作为约束. 比如, 我们想在引用上做一个封装. 还记得第15章中的 `RefCell` 吗? 它就是 `borrow` 和 `borrow_mut` 方法的工作原理; 为了在运行时追踪借用规则它们返回引用的封装. 例 19-16 中给出了一个没有生命周期参数的结构的定义: +我们在第10章里面讨论过如何在泛型上使用 trait 绑定. 我们也可以在泛型上添加生命周期参数来对它进行约束. 比如, 我们想在引用上做一个封装. 还记得第15章中的 `RefCell` 吗? 它就是 `borrow` 和 `borrow_mut` 方法的工作原理; 为了在运行时追踪借用规则它们返回引用的封装. 例 19-16 中给出了一个没有生命周期参数的结构的定义: ```rust,ignore struct Ref(&T); @@ -196,8 +196,7 @@ struct Ref(&T); 例 19-16: 先不使用生命周期参数定义一个结构来封装一个对泛型的引用 -However, using no lifetime bounds at all gives an error because Rust doesn't -know how long the generic type `T` will live: +但是, 不使用生命周期会产生编译错误, 因为 Rust 并不不知道泛型类型 `T` 会存活多久: ```text error[E0309]: the parameter type `T` may not live long enough @@ -214,31 +213,22 @@ note: ...so that the reference type `&'a T` does not outlive the data it points | ^^^^^^ ``` -This is the same error that we'd get if we filled in `T` with a concrete type, -like `struct Ref(&i32)`; all references in struct definitions need a lifetime -parameter. However, because we have a generic type parameter, we can't add a -lifetime parameter in the same way. Defining `Ref` as `struct Ref<'a>(&'a T)` -will result in an error because Rust can't determine that `T` lives long -enough. Since `T` can be any type, `T` could itself be a reference or it could -be a type that holds one or more references, each of which have their own -lifetimes. +如果我们把 `T` 换成一个具体的类型我们也会得到同样的错误, 比如像 `struct Ref(&i32)`; 在结构定义中的所有引用都需要一个生命周期参数. 然而, 因为我们有一个泛型类型参数, 所以我们不能以同样的方式来添加一个生命周期参数. 把 `Ref` 定义成 `struct Ref<'a>(&'a T)` 将会产生一个错误因为 Rust 不知道 `T` 能存活多久. 因为 `T` 可以是任意类型, `T` 本身可以是一个引用, 它也可以是一个持有一个多个引用的类型, 这些被持有的每一个引用都有它们自己的生命周期. -Rust helpfully gave us good advice on how to specify the lifetime parameter in -this case: +Rust 帮忙给了我们很好的建议, 它告诉我们在这种情况下如何使用生命周期参数: ```text consider adding an explicit lifetime bound `T: 'a` so that the reference type `&'a T` does not outlive the data it points to. ``` -The code in Listing 19-17 works because `T: 'a` syntax specifies that `T` can -be any type, but if it contains any references, `T` must live as long as `'a`: +例 19-17 中的代码可以运行因为语法 `T: 'a` 指明 `T` 可以是任意类型, 但是如果它包含任意引用, `T` 必须存活至少有 `'a` 那么长: ```rust struct Ref<'a, T: 'a>(&'a T); ``` -Listing 19-17: Adding lifetime bounds on `T` to specify +例 19-17: Adding lifetime bounds on `T` to specify that any references in `T` live at least as long as `'a` We could choose to solve this in a different way as shown in Listing 19-18 by From 430f2576b797ffa465d47e223030f0fc59c83569 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Wed, 2 Aug 2017 10:15:22 +0800 Subject: [PATCH 18/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 158 +++++++++++++----------------- 1 file changed, 67 insertions(+), 91 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index a9467ea..fb29d36 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -1,11 +1,14 @@ ## 高级生命周期 -回顾一下第10章, 我们学习了如何用生命周期注解引用参数来帮助 Rust 理解不同的引用所关联的生命周期. 我们看到大多数时候, Rust 都会让你忽略生命周期, 但是每个引用都有一个生命周期. 还有三个关于生命周期的高级特性我们以前没有介绍, 它们是: *生命周期子类型(lifetime subtyping)*, *生命周期绑定(lifetime -bounds)*, 和 *trait 对象生命周期*. +> [ch19-02-advanced-lifetimes.md](https://github.com/rust-lang/book/blob/master/second-edition/src/ch19-02-advanced-lifetimes.md) +>
+> commit d06a6a181fd61704cbf7feb55bc61d518c6469f9 + +回顾第十章,我们学习了怎样使用生命周期参数来注解引用来帮助 Rust 理解不同引用的生命周期如何相互联系。见识到了大部分情况 Rust 允许我们省略生命周期,不过每一个引用都有一个生命周期。这里有三个生命周期的高级特征我们还未讲到:**生命周期子类型**(*lifetime subtyping*),**生命周期 bound**(*lifetime bounds*),以及**trait 对象生命周期**(*trait object lifetimes*)。 ### 生命周期子类型 -想象一下我们想写一个解释器. 为此, 我们需要一个持有即将被解析的字符串的引用的结构, 我们把这个结构叫做`Context`. 我们将写一个能够解析这个字符串并返回成功或失败的解析器. 该解析器需要借用这个上下文(解析器中的`context`属性)来完成解析. 实现这个功能的代码如例 19-12, 但是这个代码不能被编译因为我们没有使用生命周期注解: +想象一下我们想要编写一个解析器。为此,会有一个储存了需要解析的字符串的引用的结构体,我们称之为结构体 `Context`。解析器将会解析字符串并返回成功或失败。解析器需要借用 `Context` 来进行解析。其实现看起来像列表 19-12 中的代码,它还不能编译,因为目前我们去掉了生命周期注解: ```rust,ignore struct Context(&str); @@ -21,11 +24,11 @@ impl Parser { } ``` -例19-12: 定义持有一个字符串切片的`Context`结构, 一个持有某个`Context`实例引用的`Parser`结构, 和一个总是返回一个错误的`parse`方法, 这个被返回的错误引用了该字符串切片 +列表 19-12:定义结构体 `Context` 来存放一个字符串 slice,结构体 `Parser` 包含一个 `Context` 实例和一个 `parse` 方法,它总是返回一个引用了字符串 slice 的错误 -为了简单起见, 我们的`parse`函数返回一个`Result<(), &str>`. 也就是说, 我们在成功时不做任何事情, 在失败时我们返回部分没有解析正确的字符串切片. 一个真正的实现将会有更多的错误信息, 而且实际上在解析成功时会返回当时创建的内容, 但是我们将实现的这部分省略了因为它们与本例的生命周期无关. 我们也定义`parse`总在第一个字节后产生一个错误. 请注意如果第一个字节不在有效的字符边界内这可能会出现错误; 再说一下, 为了把注意力放在生命周期上, 我们简化了这个例子. +为了简单起见,`parse` 方法返回 `Result<(), &str>`。也就是说,成功时不做任何操作,失败时则返回字符串 slice 没有正确解析的部分。真实的实现将会包含比这更多的错误信息,也将会在解析成功时返回创建的结果,不过我们将去掉这些部分的实现,因为他们与这个例子的生命周期部分并不相关。我们还定义了 `parse` 总是在第一个字节之后返回错误。注意如果第一个字节并不位于一个有效的字符范围内(比如 Unicode)将会 panic;我们有一次简化了例子以专注于涉及到的生命周期。 -那么我们如何设置`Context`中的字符串切片的生命周期参数和`Parser`中的`Context`引用呢? 最直接的办法就是使用同样的生命周期, 如例19-13所示: +那么我们如何为 `Context` 中的字符串 slice 和 `Parser` 中 `Context` 的引用放入生命周期参数呢?最直接的方法是在每处都使用相同的生命周期,如列表 19-13 所示: ```rust struct Context<'a>(&'a str); @@ -41,21 +44,21 @@ impl<'a> Parser<'a> { } ``` -例19-13: 限定`Context`和`Parser`中的所有引用具有同样的生命周期参数 +列表 19-13:将所有 `Context` 和 `Parser` 的引用标注为相同的生命周期参数 -这样就能够编译了. 然后, 在例19-14中, 让我们写一个以`Context`实例为参数的函数, 该函数用一个`Parser`来解析那个`Context`实例并把`parse`方法的结果直接返回. 但是这个代码不能正常工作: +这次可以编译了。接下来,在列表 19-14 中,让我们编写一个获取 `Context` 的实例,使用 `Parser` 来解析其内容,并返回 `parse` 的返回值的函数。这还不能运行: -```rust,ignore +```rust fn parse_context(context: Context) -> Result<(), &str> { Parser { context: &context }.parse() } ``` -例19-14: 尝试添加有一个`parse_context`函数, 该函数有一个`Context`参数, 在函数中使用了`Parser` +列表 19-14:一个增加获取 `Context` 并使用 `Parser` 的函数 `parse_context` 的尝试 -当我们试图用新添加的`parse_context`函数来编译代码时我们会得到两个相当详细的错误: +当尝试编译这段额外带有 `parse_context` 函数的代码时会得到两个相当冗长的错误: -```text +``` error: borrowed value does not live long enough --> :16:5 | @@ -93,35 +96,35 @@ body at 15:55... | |_^ ``` -这些错误表明不管是我们创建的`Parser`实例还是作用于从`Parser`被创建的行开始到`parse_context`函数结束的`context`参数, 都需要拥有整个函数的生命周期. +这些错误表明我们创建的两个 `Parser` 实例和 `context` 参数从 `Parser` 被创建开始一直存活到 `parse_context` 函数结束,不过他们都需要在整个函数的生命周期中都有效。 -换句话说, `Parser`和`context`存活的时间要比整个函数更长, 为了让代码中的所有引用都有效它们也应该在函数被调用前后都有效. 不管是我们正创建的`Parser`还是在函数结束时就会结束作用域的`context`参数都是如此(因为`parse_context`函数会获得`context`的所有权). +换句话说,`Parser` 和 `context` 需要比整个函数**长寿**(*outlive*)并在函数开始之前和结束之后都有效以确保代码中的所有引用始终是有效的。虽然两个我们创建的 `Parser` 和 `context` 参数在函数的结尾就离开了作用域(因为 `parse_context` 获取了 `context` 的所有权)。 -让我们再看一下例19-13中的定义, 特别是`parse`方法的声明: +让我们再次看看列表 19-13 中的定义,特别是 `parse` 方法的签名: -```rust,ignore +```rust fn parse(&self) -> Result<(), &str> { ``` -还记得生命周期的省略规则吗? 如果我们注解引用的生命周期, 那么可以这样写: +还记得(生命周期)省略规则吗?如果标注了引用生命周期,签名看起来应该是这样: -```rust,ignore +```rust fn parse<'a>(&'a self) -> Result<(), &'a str> { ``` -也就是说, `parse`函数返回值的错误中的部分是因为它有一个生命周期被绑定到了`Parser`实例的生命周期上面(也就是`parse`方法中的`&self`). 这就是了, 因为被返回的字符串切片引用了`Parser`持有的`Context`实例中的字符串切片, 并且我们已经在`Parser`结构的定义中指定了`Parser`中的`Context`的引用的生命周期和`Context`持有的字符串切片的生命周期是一样的. +正是如此,`parse` 返回值的错误部分的生命周期与 `Parser` 实例的生命周期(`parse` 方法签名中的 `&self`)相绑定。这就可以理解了,因为返回的字符串 slice 引用了 `Parser` 存放的 `Context` 实例中的字符串 slice,同时在 `Parser` 结构体的定义中我们指定了 `Parser` 中存放的 `Context` 引用的生命周期和 `Context` 中存放的字符串 slice 的生命周期应该一致。 -问题是`parse_context`函数要返回`parse`方法的返回值, 这样`parse_context`的返回值的生命周期也就被绑定到了`Parser`的生命周期上了. 但是在`parse_context`函数中创建的`Parser`实例在函数结束后就不会存活了(它是临时的), 并且`context`在函数结束后也会越过作用域(`parse_context`拥有它的所有权). +问题是 `parse_context` 函数返回 `parse` 返回值,所以 `parse_context` 返回值的生命周期也与 `Parser` 的生命周期相联系。不过 `parse_context` 函数中创建的 `Parser` 实例并不能存活到函数结束之后(它是临时的),同时 `context` 将会在函数的结尾离开作用域(`parse_context` 获取了它的所有权)。 -我们不能返回只在函数作用域内才能存活的值的引用. Rust就认为我们在做这个事情, 因为我们把所有的生命周期参数都注解成一样的了. 这就告诉 Rust `Context`持有的字符串切片的生命周期和`Parser`持有的`Context`引用的生命周期是一样的. +不允许一个在函数结尾离开作用域的值的引用。Rust 认为这是我们想要做的,因为我们将所有生命周期用相同的生命周期参数标记。这告诉了 Rust `Context` 中存放的字符串 slice 的生命周期与 `Parser` 中存放的 `Context` 引用的生命周期一致。 -`parse_context`函数看不到`parse`函数中的东西, 被返回的字符串切片的存活时间将比`Context`和`Parser`更长, 并且`parse_context`返回的是字符串切片的引用而不是`Context`和`Parser`的引用. +`parse_context` 函数并不知道 `parse` 函数里面是什么,返回的字符串 slice 将比 `Context` 和 `Parser` 都存活的更久,因此 `parse_context` 返回的引用指向字符串 slice,而不是 `Context` 或 `Parser`。 -通过了解`parse`的实现, 我们明白了`parse`的返回值被绑定到`Parser`的唯一原因是因为它引用了`Prser`中的`Context`, 也就是对一个字符串切片的引用, 所以它的生命周期才是`parse_context`需要关心的字符串切片的真正的生命周期. 我们需要告诉 Rust 在`Context`中的字符串切片和对`Parser`中的`Context`的引用有不同的生命周期, 我们还要告诉 Rust `parse_context` 函数的返回值被绑定到了`Context`中的字符串切片的生命周期. +通过了解 `parse` 实现所做的工作,可以知道 `parse` 的返回值(的生命周期)与 `Parser` 相联系的唯一理由是它引用了 `Parser` 的 `Context`,也就是引用了这个字符串 slice,这正是 `parse_context` 所需要关心的生命周期。需要一个方法来告诉 Rust `Context` 中的字符串 slice 与 `Parser` 中 `Context` 的引用有着不同的生命周期,而且 `parse_context` 返回值与 `Context` 中字符串 slice 的生命周期相联系。 -我们可以尝试像例 19-15 中显示的那样只给`Parser`和`Context`不同的生命周期参数. 我们选择了生命周期参数`'s`和`'c`, 这样可以很方便的区分哪个生命周期伴随`Context`中的字符串切片, 哪个生命周期伴随`Parser`中的`Context`引用. 注意这还不能完全修正问题, 但这是一个开始, 编译时我们就会明白为什么它还不够. +我们只能尝试像列表 19-15 那样给予 `Parser` 和 `Context` 不同的生命周期参数。这里选择了生命周期参数名 `'s` 和 `'c` 是为了使得 `Context` 中字符串 slice 与 `Parser` 中 `Context` 引用的生命周期显得更明了(英文首字母)。注意这并不能完全解决问题,不过这是一个开始,我们将看看为什么这还不足以能够编译代码。 -```rust,ignore +```rust struct Context<'s>(&'s str); struct Parser<'c, 's> { @@ -139,13 +142,13 @@ fn parse_context(context: Context) -> Result<(), &str> { } ``` -例19-15: 给对字符串切片和对`Context`的引用指定不同的生命周期参数 +列表 19-15:为字符串 slice 和 `Context` 的引用指定不同的生命周期参数 -在例 19-13 中, 我们在所有相同的地方注释了引用的生命周期, 但是是否使用不同的参数却依赖于引用是否与字符串切片或`Context`一起使用. 我们也在`parse`的返回值的字符串切片部分添加了一个生命周期注解来表明它与`Context`的生命周期一样. +这里在与列表 19-13 完全相同的地方标注了引用的生命周期,不过根据引用是字符串 slice 或 `Context` 与否使用了不同的参数。另外还在 `parse` 返回值的字符串 slice 部分增加了注解来表明它与 `Context` 中字符串 slice 的生命周期相关联。 -下面就是例19-15编译的结果: +这里是现在得到的错误: -```text +``` error[E0491]: in type `&'c Context<'s>`, reference has a longer lifetime than the data it references --> src/main.rs:4:5 | @@ -168,11 +171,11 @@ note: but the referenced data is only valid for the lifetime 's as defined on th | |_^ ``` -Rust 并不知道 `'c` 和 `'s` 之间的关系. `Context`中被引用的数据的生命周期是`'s`, 对`Context`的引用有生命周期`'c`, 为了让代码有效, 需要保证生命周期 `'s` 比生命周期 `'c` 更长. 如果 `'s` 存活的时间没有 `'c` 长, 那么对 `Context` 的引用可能就要出问题. +Rust 并不知道 `'c` 与 `'s` 之间的任何联系。为了保证有效性,`Context`中引用的带有生命周期 `'s` 的数据需要遵守它比带有生命周期 `'c` 的 `Context` 的引用存活得更久的保证。如果 `'s` 不比 `'c` 更长久,那么 `Context` 的引用可能不再有效。 -本节就是为了解决这个问题: Rust 有一个被称为 *生命周期子类型 (lifetime subtyping)* 的特性, 它可以指明一个生命周期参数的存活时间不会比另一个生命周期短. 在我们声明生命周期的尖括号里, 我们可以声明一个普通的生命周期 `'a`, 还可以通过语法 `'b: 'a` 声明一个存活时间至少和 `'a` 一样长的生命周期 `'b`. +这就引出了本部分的要点:Rust 有一个叫做**生命周期子类型**的功能,这是一个指定一个生命周期不会短于另一个的方法。在声明生命周期参数的尖括号中,可以照常声明一个生命周期 `'a`,并通过语法 `'b: 'a` 声明一个不短于 `'a` 的生命周期 `'b`。 -在我们的 `Parser` 的定义里, 为了保证 `'s`(字符串切片的生命周期) 的生命周期的存活时间至少和 `'c`(对 `Context` 引用的生命周期) 一样长, 我们把生命周期的声明改成这样: +在 `Parser` 的定义中,为了表明 `'s`(字符串 slice 的生命周期)保证至少与 `'c`(`Context` 引用的生命周期)一样长,需将生命周期声明改为如此: ```rust # struct Context<'a>(&'a str); @@ -182,86 +185,67 @@ struct Parser<'c, 's: 'c> { } ``` -现在, 对`Parser`中的`Context`的引用和对`Context`中的字符串切片的引用就有了不同的生命周期, 并且我们还保证对字符串切片的引用的生命周期比对`Context`的引用的生命周期更长. +现在 `Parser` 中 `Context` 的引用与 `Context` 中字符串 slice 就有了不同的生命周期,并且保证了字符串 slice 的生命周期比 `Context` 引用的要长。 -哦, 这个例子真的很长, 但正如本章开头所说, 这些功能非常适用. 你不会经常使用这个语法, 但是在你需要引用另一个引用中的某些内容的时候你就用得上它了. +这是一个非常冗长的例子,不过正如本章的开头所提到的,这类功能是很小众的。你并不会经常需要这个语法,不过当出现类似这样的情形时,却还是有地方可以参考的。 -### 生命周期绑定 +### 生命周期 bound -我们在第10章里面讨论过如何在泛型上使用 trait 绑定. 我们也可以在泛型上添加生命周期参数来对它进行约束. 比如, 我们想在引用上做一个封装. 还记得第15章中的 `RefCell` 吗? 它就是 `borrow` 和 `borrow_mut` 方法的工作原理; 为了在运行时追踪借用规则它们返回引用的封装. 例 19-16 中给出了一个没有生命周期参数的结构的定义: +在第十章,我们讨论了如何在泛型类型上使用 trait bound。也可以像泛型那样为生命周期参数增加限制,这被称为**生命周期 bound**。例如,考虑一下一个封装了引用的类型。回忆一下第十五章的 `RefCell` 类型:其 `borrow` 和 `borrow_mut` 方法分别返回 `Ref` 和 `RefMut` 类型。这些类型是引用的封装,他们在运行时记录检查借用规则。`Ref` 结构体的定义如列表 19-16 所示,现在还不带有生命周期 bound: -```rust,ignore -struct Ref(&T); +```rust +struct Ref<'a, T>(&'a T); ``` -例 19-16: 先不使用生命周期参数定义一个结构来封装一个对泛型的引用 +列表 19-16:定义结构体来封装泛型的引用;开始时没有生命周期 bound -但是, 不使用生命周期会产生编译错误, 因为 Rust 并不不知道泛型类型 `T` 会存活多久: +若不限制生命周期 `'a` 为与泛型参数 `T` 有关,会得到一个错误因为 Rust 不知道泛型 `T` 会存活多久: -```text +``` error[E0309]: the parameter type `T` may not live long enough - --> :2:19 + --> :1:19 | -2 | struct Ref<'a, T>(&'a T); +1 | struct Ref<'a, T>(&'a T); | ^^^^^^ | = help: consider adding an explicit lifetime bound `T: 'a`... note: ...so that the reference type `&'a T` does not outlive the data it points at - --> :2:19 + --> :1:19 | -2 | struct Ref<'a, T>(&'a T); +1 | struct Ref<'a, T>(&'a T); | ^^^^^^ ``` -如果我们把 `T` 换成一个具体的类型我们也会得到同样的错误, 比如像 `struct Ref(&i32)`; 在结构定义中的所有引用都需要一个生命周期参数. 然而, 因为我们有一个泛型类型参数, 所以我们不能以同样的方式来添加一个生命周期参数. 把 `Ref` 定义成 `struct Ref<'a>(&'a T)` 将会产生一个错误因为 Rust 不知道 `T` 能存活多久. 因为 `T` 可以是任意类型, `T` 本身可以是一个引用, 它也可以是一个持有一个多个引用的类型, 这些被持有的每一个引用都有它们自己的生命周期. +因为 `T` 可以是任意类型,`T` 自身也可能是一个引用,或者是一个存放了一个或多个引用的类型,而他们各自可能有着不同的生命周期。Rust 不能确认 `T` 会与 `'a` 存活的一样久。 -Rust 帮忙给了我们很好的建议, 它告诉我们在这种情况下如何使用生命周期参数: +幸运的是,Rust 提供了这个情况下如何指定生命周期 bound 的有用建议: -```text +``` consider adding an explicit lifetime bound `T: 'a` so that the reference type -`&'a T` does not outlive the data it points to. +`&'a T` does not outlive the data it points at. ``` -例 19-17 中的代码可以运行因为语法 `T: 'a` 指明 `T` 可以是任意类型, 但是如果它包含任意引用, `T` 必须存活至少有 `'a` 那么长: +列表 19-17 展示了按照这个建议,在声明泛型 `T` 时指定生命周期 bound。现在代码可以编译了,因为 `T: 'a` 指定了 `T` 可以为任意类型,不过如果它包含任何引用的话,其生命周期必须至少与 `'a` 一样长: ```rust struct Ref<'a, T: 'a>(&'a T); ``` -例 19-17: Adding lifetime bounds on `T` to specify -that any references in `T` live at least as long as `'a` +列表19-17:为 `T` 增加生命周期 bound 来指定 `T` 中的任何引用需至少与 `'a` 存活的一样久 -We could choose to solve this in a different way as shown in Listing 19-18 by -bounding `T` on `'static`. This means if `T` contains any references, they must -have the `'static` lifetime: +我们可以选择不同的方法来解决这个问题,如列表 19-18 中展示的 `StaticRef` 结构体定义所示,通过在 `T` 上增加 `'static` 生命周期 bound。这意味着如果 `T` 包含任何引用,他们必须有 `'static` 生命周期: ```rust struct StaticRef(&'static T); ``` -Listing 19-18: Adding a `'static` lifetime bound to `T` -to constrain `T` to types that have only `'static` references or no -references +列表 19-18:在 `T` 上增加 `'static` 生命周期 bound 来限制 `T` 为只拥有 `'static` 引用或没有引用的类型 -Types with no references count as `T: 'static`. Because `'static` means the -reference must live as long as the entire program, a type that contains no -references meets the criteria of all references living as long as the entire -program (since there are no references). Think of it this way: if the borrow -checker is concerned about references living long enough, then there's no real -distinction between a type that has no references and a type that has -references that live forever; both of them are the same for the purpose of -determining whether or not a reference has a shorter lifetime than what it -refers to. +没有任何引用的类型被算作 `T: 'static`。因为 `'static` 意味着引用必须同整个程序存活的一样长,一个不包含引用的类型满足所有引用都与程序存活的一样长的标准(因为他们没有引用)。可以这样理解:如果借用检查器关心的是引用是否存活的够久,那么没有引用的类型与有永远存在的引用的类型并没有真正的区别;对于确定引用是否比其所引用的值存活得较短的目的来说两者是一样的。 -### Trait Object Lifetimes +### trait 对象生命周期 -In Chapter 17, we learned about trait objects that consist of putting a trait -behind a reference in order to use dynamic dispatch. However, we didn't discuss -what happens if the type implementing the trait used in the trait object has a -lifetime. Consider Listing 19-19, where we have a trait `Foo` and a struct -`Bar` that holds a reference (and thus has a lifetime parameter) that -implements trait `Foo`, and we want to use an instance of `Bar` as the trait -object `Box`: +在第十七章,我们学习了 trait 对象,它包含位于引用之后的 trait 用以使用动态分发。然而,我们并没有讨论如果 trait 对象中实现 trait 的类型带有生命周期时会发生什么。考虑一下 19-19,这里有 trait `Foo`,和带有一个实现了 trait `Foo` 的引用(因此还有其生命周期参数)的结构体 `Bar`,我们希望使用 `Bar` 的实例作为 trait 对象 `Box`: ```rust trait Foo { } @@ -277,23 +261,15 @@ let num = 5; let obj = Box::new(Bar { x: &num }) as Box; ``` -Listing 19-19: Using a type that has a lifetime parameter -with a trait object +列表 19-19:使用一个带有生命周期的类型作为 trait 对象 -This code compiles without any errors, even though we haven't said anything -about the lifetimes involved in `obj`. This works because there are rules -having to do with lifetimes and trait objects: +这些代码能没有任何错误的编译,即便并没有明确指出 `obj` 中涉及的任何生命周期。这是因为有如下生命周期与 trait 对象必须遵守的规则: -* The default lifetime of a trait object is `'static`. -* If we have `&'a X` or `&'a mut X`, then the default is `'a`. -* If we have a single `T: 'a` clause, then the default is `'a`. -* If we have multiple `T: 'a`-like clauses, then there is no default; we must - be explicit. +* trait 对象的默认生命周期是 `'static`。 +* 如果有 `&'a X` 或 `&'a mut X`,则默认(生命周期)是 `'a`。 +* 如果只有 `T: 'a`, 则默认是 `'a`。 +* 如果有多个类似 `T: 'a` 的从句,则没有默认值;必须明确指定。 -When we must be explicit, we can add a lifetime bound on a trait object like -`Box` with the syntax `Box` or `Box`, depending -on what's needed. Just as with the other bounds, this means that any -implementer of the `Foo` trait that has any references inside must have the -lifetime specified in the trait object bounds as those references. +当必须明确指定时,可以为像 `Box` 这样的 trait 对象增加生命周期 bound,根据需要使用语法 `Box` 或 `Box`。正如其他的 bound,这意味着任何包含引用的实现了 `Foo` trait 的类型,其中的引用必须拥有 trait 对象所指定的生命周期。 -Next, let's take a look at some other advanced features dealing with traits! +接下来,让我们看看一些其他处理 trait 的功能吧! From 55237aeeb1b97cdaaa1efd2f5fb26bb2bb2e1c4d Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Wed, 2 Aug 2017 10:24:36 +0800 Subject: [PATCH 19/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index fb29d36..77a9d9e 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -218,7 +218,7 @@ note: ...so that the reference type `&'a T` does not outlive the data it points 因为 `T` 可以是任意类型,`T` 自身也可能是一个引用,或者是一个存放了一个或多个引用的类型,而他们各自可能有着不同的生命周期。Rust 不能确认 `T` 会与 `'a` 存活的一样久。 -幸运的是,Rust 提供了这个情况下如何指定生命周期 bound 的有用建议: +幸运的是,Rust 在这种情况下给出了如何指定生命周期 bound 的好建议: ``` consider adding an explicit lifetime bound `T: 'a` so that the reference type From 92285c2973fe084fee2e04841d4924e8a8a88808 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Wed, 2 Aug 2017 14:27:39 +0800 Subject: [PATCH 20/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index 77a9d9e..c5c1f28 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -241,7 +241,7 @@ struct StaticRef(&'static T); 列表 19-18:在 `T` 上增加 `'static` 生命周期 bound 来限制 `T` 为只拥有 `'static` 引用或没有引用的类型 -没有任何引用的类型被算作 `T: 'static`。因为 `'static` 意味着引用必须同整个程序存活的一样长,一个不包含引用的类型满足所有引用都与程序存活的一样长的标准(因为他们没有引用)。可以这样理解:如果借用检查器关心的是引用是否存活的够久,那么没有引用的类型与有永远存在的引用的类型并没有真正的区别;对于确定引用是否比其所引用的值存活得较短的目的来说两者是一样的。 +没有任何引用的类型就计为 `T: 'static`。因为 `'static` 意味着引用必须同整个程序存活得一样长,一个不包含引用的类型满足所有引用都与程序存活得一样长的标准(因为他们没有引用)。可以这样理解:如果借用检查器关心的是引用能存活多久,那么没有引用的类型与有引用且引用能一直存活的类型并没有真正的区别;对于确定引用是否比其所引用的值存活得较短的目的来说两者是一样的。 ### trait 对象生命周期 From 6a7be5d144e68adefd6f971887d7ec906e466286 Mon Sep 17 00:00:00 2001 From: Librazy Date: Wed, 2 Aug 2017 22:47:52 +0800 Subject: [PATCH 21/23] fix translation about `Copy` ...and nothing that requires allocation or is some form of resource is `Copy`. --- src/ch04-01-what-is-ownership.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ch04-01-what-is-ownership.md b/src/ch04-01-what-is-ownership.md index 3a5abd5..32bb321 100644 --- a/src/ch04-01-what-is-ownership.md +++ b/src/ch04-01-what-is-ownership.md @@ -245,7 +245,7 @@ println!("x = {}, y = {}", x, y); Rust 有一个叫做`Copy` trait 的特殊注解,可以用在类似整型这样的储存在栈上的类型(第十章详细讲解 trait)。如果一个类型拥有`Copy` trait,一个旧的变量在(重新)赋值后仍然可用。Rust 不允许自身或其任何部分实现了`Drop` trait 的类型使用`Copy` trait。如果我们对其值离开作用域时需要特殊处理的类型使用`Copy`注解,将会出现一个编译时错误。关于如何为你的类型增加`Copy`注解,请阅读附录 C 中的 Derivable Trait。 -那么什么类型是`Copy`的呢?可以查看给定类型的文档来确认,不过作为一个通用的规则,任何简单标量值的组合可以是`Copy`的,任何不需要分配内存或类似形式资源的类型是`Copy`的,如下是一些`Copy`的类型: +那么什么类型是`Copy`的呢?可以查看给定类型的文档来确认,不过作为一个通用的规则,任何简单标量值的组合可以是`Copy`的,任何需要分配内存,或者本身就是某种形式资源的类型不会是`Copy`的。如下是一些`Copy`的类型: * 所有整数类型,比如`u32`。 * 布尔类型,`bool`,它的值是`true`和`false`。 From 3e7e3e4ed85103fda317e840a842d86e5c36a696 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Thu, 3 Aug 2017 10:15:44 +0800 Subject: [PATCH 22/23] Update ch19-02-advanced-lifetimes.md --- src/ch19-02-advanced-lifetimes.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ch19-02-advanced-lifetimes.md b/src/ch19-02-advanced-lifetimes.md index fb29d36..bdcfca2 100644 --- a/src/ch19-02-advanced-lifetimes.md +++ b/src/ch19-02-advanced-lifetimes.md @@ -245,7 +245,7 @@ struct StaticRef(&'static T); ### trait 对象生命周期 -在第十七章,我们学习了 trait 对象,它包含位于引用之后的 trait 用以使用动态分发。然而,我们并没有讨论如果 trait 对象中实现 trait 的类型带有生命周期时会发生什么。考虑一下 19-19,这里有 trait `Foo`,和带有一个实现了 trait `Foo` 的引用(因此还有其生命周期参数)的结构体 `Bar`,我们希望使用 `Bar` 的实例作为 trait 对象 `Box`: +在第十七章,我们学习了 trait 对象,其中介绍了可以把一个 trait 放在一个引用后面来进行动态分发。然而,我们并没有讨论如果 trait 对象中实现 trait 的类型带有生命周期时会发生什么。考虑一下 19-19,这里有 trait `Foo`,和带有一个实现了 trait `Foo` 的引用(因此还有其生命周期参数)的结构体 `Bar`,我们希望使用 `Bar` 的实例作为 trait 对象 `Box`: ```rust trait Foo { } @@ -270,6 +270,6 @@ let obj = Box::new(Bar { x: &num }) as Box; * 如果只有 `T: 'a`, 则默认是 `'a`。 * 如果有多个类似 `T: 'a` 的从句,则没有默认值;必须明确指定。 -当必须明确指定时,可以为像 `Box` 这样的 trait 对象增加生命周期 bound,根据需要使用语法 `Box` 或 `Box`。正如其他的 bound,这意味着任何包含引用的实现了 `Foo` trait 的类型,其中的引用必须拥有 trait 对象所指定的生命周期。 +当必须明确指定时,可以为像 `Box` 这样的 trait 对象增加生命周期 bound,根据需要使用语法 `Box` 或 `Box`。正如其他的 bound,这意味着任何 `Foo` trait 的实现如果在内部包含有引用, 就必须在 trait 对象 bounds 中为那些引用指定生命周期。 接下来,让我们看看一些其他处理 trait 的功能吧! From b2c3856a134e2237653829ed6d3b516c87c9a5a1 Mon Sep 17 00:00:00 2001 From: Zheng Ping Date: Thu, 3 Aug 2017 14:17:40 +0800 Subject: [PATCH 23/23] Update ch19-03-advanced-traits.md --- src/ch19-03-advanced-traits.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ch19-03-advanced-traits.md b/src/ch19-03-advanced-traits.md index 18e26cb..855d452 100644 --- a/src/ch19-03-advanced-traits.md +++ b/src/ch19-03-advanced-traits.md @@ -8,9 +8,9 @@ ### 关联类型 -**关联类型**(*associated types*)是一个将类型占位符与 trait 相关联的方法,如此 trait 的方法定义的签名中就可以使用这些占位符类型。实现 trait 的类型将会在特定实现中指定所用的具体类型。 +**关联类型**(*associated types*)是一个将类型占位符与 trait 相关联的方式,这样 trait 的方法签名中就可以使用这些占位符类型。实现一个 trait 的人只需要针对专门的实现在这个类型的位置指定相应的类型即可。 -本章描述的大部分内容都非常少见。关联类型则比较适中;他们比本书其他的内容要少见,不过比本章很多的内容要更常见。 +本章描述的大部分内容都非常少见。关联类型则比较适中;他们比本书其他的内容要少见,不过比本章中的很多内容要更常见。 一个带有关联类型的 trait 的例子是标准库提供的 `Iterator` trait。它有一个叫做 `Item` 的关联类型来替代遍历的值的类型。第十三章曾提到过 `Iterator` trait 的定义如列表 19-20 所示: @@ -23,7 +23,7 @@ pub trait Iterator { 列表 19-20:`Iterator` trait 的定义中带有关联类型 `Item` -这就是说 `Iterator` trait 有一个关联类型 `Item`。`Item` 是一个占位类型,同时 `next` 方法会返回 `Option` 类型的值。这个 trait 的实现者会指定 `Item` 的具体类型,而 `next` 方法会返回一个 `Option` 包含无论实现者指定的何种类型的值。 +这就是说 `Iterator` trait 有一个关联类型 `Item`。`Item` 是一个占位类型,同时 `next` 方法会返回 `Option` 类型的值。这个 trait 的实现者会指定 `Item` 的具体类型,而不管实现者指定何种类型, `next` 方法都会返回一个包含了这种类型值的 `Option`。 #### 关联类型 vs 泛型