]>
Commit | Line | Data |
---|---|---|
cc61c64b XL |
1 | ## Advanced Lifetimes |
2 | ||
3 | Back in Chapter 10, we learned how to annotate references with lifetime | |
4 | parameters to help Rust understand how the lifetimes of different references | |
5 | relate. We saw how most of the time, Rust will let you elide lifetimes, but | |
6 | every reference has a lifetime. There are three advanced features of lifetimes | |
7cac9316 XL |
7 | that we haven't covered though: *lifetime subtyping*, *lifetime |
8 | bounds*, and *trait object lifetimes*. | |
cc61c64b XL |
9 | |
10 | ### Lifetime Subtyping | |
11 | ||
12 | Imagine that we want to write a parser. To do this, we'll have a structure that | |
13 | holds a reference to the string that we're parsing, and we'll call that struct | |
14 | `Context`. We'll write a parser that will parse this string and return success | |
15 | or failure. The parser will need to borrow the context to do the parsing. | |
16 | Implementing this would look like the code in Listing 19-12, which won't | |
17 | compile because we've left off the lifetime annotations for now: | |
18 | ||
19 | ```rust,ignore | |
20 | struct Context(&str); | |
21 | ||
22 | struct Parser { | |
23 | context: &Context, | |
24 | } | |
25 | ||
26 | impl Parser { | |
27 | fn parse(&self) -> Result<(), &str> { | |
28 | Err(&self.context.0[1..]) | |
29 | } | |
30 | } | |
31 | ``` | |
32 | ||
33 | <span class="caption">Listing 19-12: Defining a `Context` struct that holds a | |
34 | string slice, a `Parser` struct that holds a reference to a `Context` instance, | |
35 | and a `parse` method that always returns an error referencing the string | |
36 | slice</span> | |
37 | ||
38 | For simplicity's sake, our `parse` function returns a `Result<(), &str>`. That | |
39 | is, we don't do anything on success, and on failure we return the part of the | |
40 | string slice that didn't parse correctly. A real implementation would have more | |
41 | error information than that, and would actually return something created when | |
42 | parsing succeeds, but we're leaving those parts of the implementation off since | |
43 | they aren't relevant to the lifetimes part of this example. We're also defining | |
44 | `parse` to always produce an error after the first byte. Note that this may | |
45 | panic if the first byte is not on a valid character boundary; again, we're | |
46 | simplifying the example in order to concentrate on the lifetimes involved. | |
47 | ||
48 | So how do we fill in the lifetime parameters for the string slice in `Context` | |
49 | and the reference to the `Context` in `Parser`? The most straightforward thing | |
50 | to do is to use the same lifetime everywhere, as shown in Listing 19-13: | |
51 | ||
52 | ```rust | |
53 | struct Context<'a>(&'a str); | |
54 | ||
55 | struct Parser<'a> { | |
56 | context: &'a Context<'a>, | |
57 | } | |
58 | ||
59 | impl<'a> Parser<'a> { | |
60 | fn parse(&self) -> Result<(), &str> { | |
61 | Err(&self.context.0[1..]) | |
62 | } | |
63 | } | |
64 | ``` | |
65 | ||
66 | <span class="caption">Listing 19-13: Annotating all references in `Context` and | |
67 | `Parser` with the same lifetime parameter</span> | |
68 | ||
69 | This compiles fine. Next, in Listing 19-14, let's write a function that takes | |
70 | an instance of `Context`, uses a `Parser` to parse that context, and returns | |
71 | what `parse` returns. This won't quite work: | |
72 | ||
73 | ```rust,ignore | |
74 | fn parse_context(context: Context) -> Result<(), &str> { | |
75 | Parser { context: &context }.parse() | |
76 | } | |
77 | ``` | |
78 | ||
79 | <span class="caption">Listing 19-14: An attempt to add a `parse_context` | |
80 | function that takes a `Context` and uses a `Parser`</span> | |
81 | ||
82 | We get two quite verbose errors when we try to compile the code with the | |
83 | addition of the `parse_context` function: | |
84 | ||
85 | ```text | |
86 | error: borrowed value does not live long enough | |
87 | --> <anon>:16:5 | |
88 | | | |
89 | 16 | Parser { context: &context }.parse() | |
90 | | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough | |
91 | 17 | } | |
92 | | - temporary value only lives until here | |
93 | | | |
94 | note: borrowed value must be valid for the anonymous lifetime #1 defined on the | |
95 | body at 15:55... | |
96 | --> <anon>:15:56 | |
97 | | | |
98 | 15 | fn parse_context(context: Context) -> Result<(), &str> { | |
7cac9316 | 99 | | ________________________________________________________^ |
cc61c64b XL |
100 | 16 | | Parser { context: &context }.parse() |
101 | 17 | | } | |
7cac9316 | 102 | | |_^ |
cc61c64b XL |
103 | |
104 | error: `context` does not live long enough | |
105 | --> <anon>:16:24 | |
106 | | | |
107 | 16 | Parser { context: &context }.parse() | |
108 | | ^^^^^^^ does not live long enough | |
109 | 17 | } | |
110 | | - borrowed value only lives until here | |
111 | | | |
112 | note: borrowed value must be valid for the anonymous lifetime #1 defined on the | |
113 | body at 15:55... | |
114 | --> <anon>:15:56 | |
115 | | | |
116 | 15 | fn parse_context(context: Context) -> Result<(), &str> { | |
7cac9316 | 117 | | ________________________________________________________^ |
cc61c64b XL |
118 | 16 | | Parser { context: &context }.parse() |
119 | 17 | | } | |
7cac9316 | 120 | | |_^ |
cc61c64b XL |
121 | ``` |
122 | ||
123 | These errors are saying that both the `Parser` instance we're creating and the | |
124 | `context` parameter live from the line that the `Parser` is created until the | |
125 | end of the `parse_context` function, but they both need to live for the entire | |
126 | lifetime of the function. | |
127 | ||
128 | In other words, `Parser` and `context` need to *outlive* the entire function | |
129 | and be valid before the function starts as well as after it ends in order for | |
130 | all the references in this code to always be valid. Both the `Parser` we're | |
131 | creating and the `context` parameter go out of scope at the end of the | |
132 | function, though (since `parse_context` takes ownership of `context`). | |
133 | ||
134 | Let's look at the definitions in Listing 19-13 again, especially the signature | |
135 | of the `parse` method: | |
136 | ||
137 | ```rust,ignore | |
138 | fn parse(&self) -> Result<(), &str> { | |
139 | ``` | |
140 | ||
141 | Remember the elision rules? If we annotate the lifetimes of the references, the | |
142 | signature would be: | |
143 | ||
144 | ```rust,ignore | |
145 | fn parse<'a>(&'a self) -> Result<(), &'a str> { | |
146 | ``` | |
147 | ||
148 | That is, the error part of the return value of `parse` has a lifetime that is | |
149 | tied to the `Parser` instance's lifetime (that of `&self` in the `parse` method | |
150 | signature). That makes sense, as the returned string slice references the | |
151 | string slice in the `Context` instance that the `Parser` holds, and we've | |
152 | specified in the definition of the `Parser` struct that the lifetime of the | |
153 | reference to `Context` that `Parser` holds and the lifetime of the string slice | |
154 | that `Context` holds should be the same. | |
155 | ||
156 | The problem is that the `parse_context` function returns the value returned | |
157 | from `parse`, so the lifetime of the return value of `parse_context` is tied to | |
158 | the lifetime of the `Parser` as well. But the `Parser` instance created in the | |
159 | `parse_context` function won't live past the end of the function (it's | |
160 | temporary), and the `context` will go out of scope at the end of the function | |
161 | (`parse_context` takes ownership of it). | |
162 | ||
163 | We're not allowed to return a reference to a value that goes out of scope at | |
164 | the end of the function. Rust thinks that's what we're trying to do because we | |
165 | annotated all the lifetimes with the same lifetime parameter. That told Rust | |
166 | the lifetime of the string slice that `Context` holds is the same as that of | |
167 | the lifetime of the reference to `Context` that `Parser` holds. | |
168 | ||
169 | The `parse_context` function can't see that within the `parse` function, the | |
170 | string slice returned will outlive both `Context` and `Parser`, and that the | |
171 | reference `parse_context` returns refers to the string slice, not to `Context` | |
172 | or `Parser`. | |
173 | ||
174 | By knowing what the implementation of `parse` does, we know that the only | |
175 | reason that the return value of `parse` is tied to the `Parser` is because it's | |
176 | referencing the `Parser`'s `Context`, which is referencing the string slice, so | |
177 | it's really the lifetime of the string slice that `parse_context` needs to care | |
178 | about. We need a way to tell Rust that the string slice in `Context` and the | |
179 | reference to the `Context` in `Parser` have different lifetimes and that the | |
180 | return value of `parse_context` is tied to the lifetime of the string slice in | |
181 | `Context`. | |
182 | ||
183 | We could try only giving `Parser` and `Context` different lifetime parameters | |
184 | as shown in Listing 19-15. We've chosen the lifetime parameter names `'s` and | |
185 | `'c` here to be clearer about which lifetime goes with the string slice in | |
186 | `Context` and which goes with the reference to `Context` in `Parser`. Note that | |
187 | this won't completely fix the problem, but it's a start and we'll look at why | |
188 | this isn't sufficient when we try to compile. | |
189 | ||
190 | ```rust,ignore | |
191 | struct Context<'s>(&'s str); | |
192 | ||
193 | struct Parser<'c, 's> { | |
194 | context: &'c Context<'s>, | |
195 | } | |
196 | ||
197 | impl<'c, 's> Parser<'c, 's> { | |
198 | fn parse(&self) -> Result<(), &'s str> { | |
199 | Err(&self.context.0[1..]) | |
200 | } | |
201 | } | |
202 | ||
203 | fn parse_context(context: Context) -> Result<(), &str> { | |
204 | Parser { context: &context }.parse() | |
205 | } | |
206 | ``` | |
207 | ||
208 | <span class="caption">Listing 19-15: Specifying different lifetime parameters | |
209 | for the references to the string slice and to `Context`</span> | |
210 | ||
211 | We've annotated the lifetimes of the references in all the same places that we | |
212 | annotated them in Listing 19-13, but used different parameters depending on | |
213 | whether the reference goes with the string slice or with `Context`. We've also | |
214 | added an annotation to the string slice part of the return value of `parse` to | |
215 | indicate that it goes with the lifetime of the string slice in `Context`. | |
216 | ||
217 | Here's the error we get now: | |
218 | ||
219 | ```text | |
220 | error[E0491]: in type `&'c Context<'s>`, reference has a longer lifetime than the data it references | |
221 | --> src/main.rs:4:5 | |
222 | | | |
223 | 4 | context: &'c Context<'s>, | |
224 | | ^^^^^^^^^^^^^^^^^^^^^^^^ | |
225 | | | |
226 | note: the pointer is valid for the lifetime 'c as defined on the struct at 3:0 | |
227 | --> src/main.rs:3:1 | |
228 | | | |
7cac9316 | 229 | 3 | / struct Parser<'c, 's> { |
cc61c64b XL |
230 | 4 | | context: &'c Context<'s>, |
231 | 5 | | } | |
7cac9316 | 232 | | |_^ |
cc61c64b XL |
233 | note: but the referenced data is only valid for the lifetime 's as defined on the struct at 3:0 |
234 | --> src/main.rs:3:1 | |
235 | | | |
7cac9316 | 236 | 3 | / struct Parser<'c, 's> { |
cc61c64b XL |
237 | 4 | | context: &'c Context<'s>, |
238 | 5 | | } | |
7cac9316 | 239 | | |_^ |
cc61c64b XL |
240 | ``` |
241 | ||
242 | Rust doesn't know of any relationship between `'c` and `'s`. In order to be | |
243 | valid, the referenced data in `Context` with lifetime `'s` needs to be | |
244 | constrained to guarantee that it lives longer than the reference to `Context` | |
245 | that has lifetime `'c`. If `'s` is not longer than `'c`, then the reference to | |
246 | `Context` might not be valid. | |
247 | ||
248 | Which gets us to the point of this section: Rust has a feature called *lifetime | |
249 | subtyping*, which is a way to specify that one lifetime parameter lives at | |
250 | least as long as another one. In the angle brackets where we declare lifetime | |
251 | parameters, we can declare a lifetime `'a` as usual, and declare a lifetime | |
252 | `'b` that lives at least as long as `'a` by declaring `'b` with the syntax `'b: | |
253 | 'a`. | |
254 | ||
255 | In our definition of `Parser`, in order to say that `'s` (the lifetime of the | |
256 | string slice) is guaranteed to live at least as long as `'c` (the lifetime of | |
257 | the reference to `Context`), we change the lifetime declarations to look like | |
258 | this: | |
259 | ||
260 | ```rust | |
261 | # struct Context<'a>(&'a str); | |
262 | # | |
263 | struct Parser<'c, 's: 'c> { | |
264 | context: &'c Context<'s>, | |
265 | } | |
266 | ``` | |
267 | ||
268 | Now, the reference to `Context` in the `Parser` and the reference to the string | |
269 | slice in the `Context` have different lifetimes, and we've ensured that the | |
270 | lifetime of the string slice is longer than the reference to the `Context`. | |
271 | ||
272 | That was a very long-winded example, but as we mentioned at the start of this | |
273 | chapter, these features are pretty niche. You won't often need this syntax, but | |
274 | it can come up in situations like this one, where you need to refer to | |
275 | something you have a reference to. | |
276 | ||
277 | ### Lifetime Bounds | |
278 | ||
279 | In Chapter 10, we discussed how to use trait bounds on generic types. We can | |
280 | also add lifetime parameters as constraints on generic types. For example, | |
281 | let's say we wanted to make a wrapper over references. Remember `RefCell<T>` | |
282 | from Chapter 15? This is how the `borrow` and `borrow_mut` methods work; they | |
283 | return wrappers over references in order to keep track of the borrowing rules | |
284 | at runtime. The struct definition, without lifetime parameters for now, would | |
285 | look like Listing 19-16: | |
286 | ||
287 | ```rust,ignore | |
288 | struct Ref<T>(&T); | |
289 | ``` | |
290 | ||
291 | <span class="caption">Listing 19-16: Defining a struct to wrap a reference to a | |
292 | generic type; without lifetime parameters to start</span> | |
293 | ||
294 | However, using no lifetime bounds at all gives an error because Rust doesn't | |
295 | know how long the generic type `T` will live: | |
296 | ||
297 | ```text | |
298 | error[E0309]: the parameter type `T` may not live long enough | |
299 | --> <anon>:2:19 | |
300 | | | |
301 | 2 | struct Ref<'a, T>(&'a T); | |
302 | | ^^^^^^ | |
303 | | | |
304 | = help: consider adding an explicit lifetime bound `T: 'a`... | |
305 | note: ...so that the reference type `&'a T` does not outlive the data it points at | |
306 | --> <anon>:2:19 | |
307 | | | |
308 | 2 | struct Ref<'a, T>(&'a T); | |
309 | | ^^^^^^ | |
310 | ``` | |
311 | ||
312 | This is the same error that we'd get if we filled in `T` with a concrete type, | |
313 | like `struct Ref(&i32)`; all references in struct definitions need a lifetime | |
314 | parameter. However, because we have a generic type parameter, we can't add a | |
315 | lifetime parameter in the same way. Defining `Ref` as `struct Ref<'a>(&'a T)` | |
316 | will result in an error because Rust can't determine that `T` lives long | |
317 | enough. Since `T` can be any type, `T` could itself be a reference or it could | |
318 | be a type that holds one or more references, each of which have their own | |
319 | lifetimes. | |
320 | ||
321 | Rust helpfully gave us good advice on how to specify the lifetime parameter in | |
322 | this case: | |
323 | ||
324 | ```text | |
325 | consider adding an explicit lifetime bound `T: 'a` so that the reference type | |
326 | `&'a T` does not outlive the data it points to. | |
327 | ``` | |
328 | ||
329 | The code in Listing 19-17 works because `T: 'a` syntax specifies that `T` can | |
330 | be any type, but if it contains any references, `T` must live as long as `'a`: | |
331 | ||
332 | ```rust | |
333 | struct Ref<'a, T: 'a>(&'a T); | |
334 | ``` | |
335 | ||
336 | <span class="caption">Listing 19-17: Adding lifetime bounds on `T` to specify | |
337 | that any references in `T` live at least as long as `'a`</span> | |
338 | ||
339 | We could choose to solve this in a different way as shown in Listing 19-18 by | |
340 | bounding `T` on `'static`. This means if `T` contains any references, they must | |
341 | have the `'static` lifetime: | |
342 | ||
343 | ```rust | |
344 | struct StaticRef<T: 'static>(&'static T); | |
345 | ``` | |
346 | ||
347 | <span class="caption">Listing 19-18: Adding a `'static` lifetime bound to `T` | |
348 | to constrain `T` to types that have only `'static` references or no | |
349 | references</span> | |
350 | ||
351 | Types with no references count as `T: 'static`. Because `'static` means the | |
352 | reference must live as long as the entire program, a type that contains no | |
353 | references meets the criteria of all references living as long as the entire | |
354 | program (since there are no references). Think of it this way: if the borrow | |
355 | checker is concerned about references living long enough, then there's no real | |
356 | distinction between a type that has no references and a type that has | |
357 | references that live forever; both of them are the same for the purpose of | |
358 | determining whether or not a reference has a shorter lifetime than what it | |
359 | refers to. | |
360 | ||
7cac9316 | 361 | ### Trait Object Lifetimes |
cc61c64b XL |
362 | |
363 | In Chapter 17, we learned about trait objects that consist of putting a trait | |
364 | behind a reference in order to use dynamic dispatch. However, we didn't discuss | |
365 | what happens if the type implementing the trait used in the trait object has a | |
366 | lifetime. Consider Listing 19-19, where we have a trait `Foo` and a struct | |
367 | `Bar` that holds a reference (and thus has a lifetime parameter) that | |
368 | implements trait `Foo`, and we want to use an instance of `Bar` as the trait | |
369 | object `Box<Foo>`: | |
370 | ||
371 | ```rust | |
372 | trait Foo { } | |
373 | ||
374 | struct Bar<'a> { | |
375 | x: &'a i32, | |
376 | } | |
377 | ||
378 | impl<'a> Foo for Bar<'a> { } | |
379 | ||
380 | let num = 5; | |
381 | ||
382 | let obj = Box::new(Bar { x: &num }) as Box<Foo>; | |
383 | ``` | |
384 | ||
385 | <span class="caption">Listing 19-19: Using a type that has a lifetime parameter | |
386 | with a trait object</span> | |
387 | ||
388 | This code compiles without any errors, even though we haven't said anything | |
389 | about the lifetimes involved in `obj`. This works because there are rules | |
390 | having to do with lifetimes and trait objects: | |
391 | ||
392 | * The default lifetime of a trait object is `'static`. | |
393 | * If we have `&'a X` or `&'a mut X`, then the default is `'a`. | |
394 | * If we have a single `T: 'a` clause, then the default is `'a`. | |
395 | * If we have multiple `T: 'a`-like clauses, then there is no default; we must | |
396 | be explicit. | |
397 | ||
398 | When we must be explicit, we can add a lifetime bound on a trait object like | |
399 | `Box<Foo>` with the syntax `Box<Foo + 'a>` or `Box<Foo + 'static>`, depending | |
400 | on what's needed. Just as with the other bounds, this means that any | |
401 | implementer of the `Foo` trait that has any references inside must have the | |
402 | lifetime specified in the trait object bounds as those references. | |
403 | ||
404 | Next, let's take a look at some other advanced features dealing with traits! |