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
7 that we haven't covered though: *lifetime subtyping*, *lifetime
8 bounds*, and *trait object lifetimes*.
10 ### Lifetime Subtyping
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:
27 fn parse(&self) -> Result<(), &str> {
28 Err(&self.context.0[1..])
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
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.
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:
53 struct Context<'a>(&'a str);
56 context: &'a Context<'a>,
60 fn parse(&self) -> Result<(), &str> {
61 Err(&self.context.0[1..])
66 <span class="caption">Listing 19-13: Annotating all references in `Context` and
67 `Parser` with the same lifetime parameter</span>
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:
74 fn parse_context(context: Context) -> Result<(), &str> {
75 Parser { context: &context }.parse()
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>
82 We get two quite verbose errors when we try to compile the code with the
83 addition of the `parse_context` function:
86 error: borrowed value does not live long enough
89 16 | Parser { context: &context }.parse()
90 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ does not live long enough
92 | - temporary value only lives until here
94 note: borrowed value must be valid for the anonymous lifetime #1 defined on the
98 15 | fn parse_context(context: Context) -> Result<(), &str> {
99 | ________________________________________________________^
100 16 | | Parser { context: &context }.parse()
104 error: `context` does not live long enough
107 16 | Parser { context: &context }.parse()
108 | ^^^^^^^ does not live long enough
110 | - borrowed value only lives until here
112 note: borrowed value must be valid for the anonymous lifetime #1 defined on the
116 15 | fn parse_context(context: Context) -> Result<(), &str> {
117 | ________________________________________________________^
118 16 | | Parser { context: &context }.parse()
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.
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`).
134 Let's look at the definitions in Listing 19-13 again, especially the signature
135 of the `parse` method:
138 fn parse(&self) -> Result<(), &str> {
141 Remember the elision rules? If we annotate the lifetimes of the references, the
145 fn parse<'a>(&'a self) -> Result<(), &'a str> {
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.
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).
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.
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`
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
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.
191 struct Context<'s>(&'s str);
193 struct Parser<'c, 's> {
194 context: &'c Context<'s>,
197 impl<'c, 's> Parser<'c, 's> {
198 fn parse(&self) -> Result<(), &'s str> {
199 Err(&self.context.0[1..])
203 fn parse_context(context: Context) -> Result<(), &str> {
204 Parser { context: &context }.parse()
208 <span class="caption">Listing 19-15: Specifying different lifetime parameters
209 for the references to the string slice and to `Context`</span>
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`.
217 Here's the error we get now:
220 error[E0491]: in type `&'c Context<'s>`, reference has a longer lifetime than the data it references
223 4 | context: &'c Context<'s>,
224 | ^^^^^^^^^^^^^^^^^^^^^^^^
226 note: the pointer is valid for the lifetime 'c as defined on the struct at 3:0
229 3 | / struct Parser<'c, 's> {
230 4 | | context: &'c Context<'s>,
233 note: but the referenced data is only valid for the lifetime 's as defined on the struct at 3:0
236 3 | / struct Parser<'c, 's> {
237 4 | | context: &'c Context<'s>,
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.
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:
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
261 # struct Context<'a>(&'a str);
263 struct Parser<'c, 's: 'c> {
264 context: &'c Context<'s>,
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`.
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.
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, which are called
281 *lifetime bounds*. For example, consider a type that is a wrapper over
282 references. Recall the `RefCell<T>` type from Chapter 15: its `borrow` and
283 `borrow_mut` methods return the types `Ref` and `RefMut`, respectively. These
284 types are wrappers over references that keep track of the borrowing rules at
285 runtime. The definition of the `Ref` struct is shown in Listing 19-16, without
286 lifetime bounds for now:
289 struct Ref<'a, T>(&'a T);
292 <span class="caption">Listing 19-16: Defining a struct to wrap a reference to a
293 generic type; without lifetime bounds to start</span>
295 Without constraining the lifetime `'a` in relation to the generic parameter
296 `T`, we get an error because Rust doesn't know how long the generic type `T`
300 error[E0309]: the parameter type `T` may not live long enough
303 1 | struct Ref<'a, T>(&'a T);
306 = help: consider adding an explicit lifetime bound `T: 'a`...
307 note: ...so that the reference type `&'a T` does not outlive the data it points at
310 1 | struct Ref<'a, T>(&'a T);
314 Since `T` can be any type, `T` could itself be a reference or a type that holds
315 one or more references, each of which could have their own lifetimes. Rust
316 can't be sure `T` will live as long as `'a`.
318 Fortunately, Rust gave us helpful advice on how to specify the lifetime bound in
322 consider adding an explicit lifetime bound `T: 'a` so that the reference type
323 `&'a T` does not outlive the data it points at.
326 Listing 19-17 shows how to apply this advice by specifying the lifetime bound
327 when we declare the generic type `T`. This code now compiles because the `T:
328 'a` syntax specifies that `T` can be any type, but if it contains any
329 references, the references must live at least as long as `'a`:
332 struct Ref<'a, T: 'a>(&'a T);
335 <span class="caption">Listing 19-17: Adding lifetime bounds on `T` to specify
336 that any references in `T` live at least as long as `'a`</span>
338 We could choose to solve this in a different way, shown in the definition of a
339 `StaticRef` struct in Listing 19-18, by adding the `'static` lifetime bound on
340 `T`. This means if `T` contains any references, they must have the `'static`
344 struct StaticRef<T: 'static>(&'static T);
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
351 Types without any 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
361 ### Trait Object Lifetimes
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
378 impl<'a> Foo for Bar<'a> { }
382 let obj = Box::new(Bar { x: &num }) as Box<Foo>;
385 <span class="caption">Listing 19-19: Using a type that has a lifetime parameter
386 with a trait object</span>
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:
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
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.
404 Next, let's take a look at some other advanced features dealing with traits!