3 Rust enforces these rules through *lifetimes*. Lifetimes are named
4 regions of code that a reference must be valid for. Those regions
5 may be fairly complex, as they correspond to paths of execution
6 in the program. There may even be holes in these paths of execution,
7 as it's possible to invalidate a reference as long as it's reinitialized
8 before it's used again. Types which contain references (or pretend to)
9 may also be tagged with lifetimes so that Rust can prevent them from
10 being invalidated as well.
12 In most of our examples, the lifetimes will coincide with scopes. This is
13 because our examples are simple. The more complex cases where they don't
14 coincide are described below.
16 Within a function body, Rust generally doesn't let you explicitly name the
17 lifetimes involved. This is because it's generally not really necessary
18 to talk about lifetimes in a local context; Rust has all the information and
19 can work out everything as optimally as possible. Many anonymous scopes and
20 temporaries that you would otherwise have to write are often introduced to
21 make your code Just Work.
23 However once you cross the function boundary, you need to start talking about
24 lifetimes. Lifetimes are denoted with an apostrophe: `'a`, `'static`. To dip
25 our toes with lifetimes, we're going to pretend that we're actually allowed
26 to label scopes with lifetimes, and desugar the examples from the start of
29 Originally, our examples made use of *aggressive* sugar -- high fructose corn
30 syrup even -- around scopes and lifetimes, because writing everything out
31 explicitly is *extremely noisy*. All Rust code relies on aggressive inference
32 and elision of "obvious" things.
34 One particularly interesting piece of sugar is that each `let` statement
35 implicitly introduces a scope. For the most part, this doesn't really matter.
36 However it does matter for variables that refer to each other. As a simple
37 example, let's completely desugar this simple piece of Rust code:
45 The borrow checker always tries to minimize the extent of a lifetime, so it will
46 likely desugar to the following:
49 // NOTE: `'a: {` and `&'b x` is not valid syntax!
53 // lifetime used is 'b because that's good enough.
54 let y: &'b i32 = &'b x;
57 let z: &'c &'b i32 = &'c y;
63 Wow. That's... awful. Let's all take a moment to thank Rust for making this easier.
65 Actually passing references to outer scopes will cause Rust to infer
81 // Must use 'b here because this reference is
82 // being passed to that scope.
83 let y: &'b i32 = &'b x;
92 # Example: references that outlive referents
94 Alright, let's look at some of those examples from before:
97 fn as_str(data: &u32) -> &str {
98 let s = format!("{}", data);
106 fn as_str<'a>(data: &'a u32) -> &'a str {
108 let s = format!("{}", data);
114 This signature of `as_str` takes a reference to a u32 with *some* lifetime, and
115 promises that it can produce a reference to a str that can live *just as long*.
116 Already we can see why this signature might be trouble. That basically implies
117 that we're going to find a str somewhere in the scope the reference
118 to the u32 originated in, or somewhere *even earlier*. That's a bit of a tall
121 We then proceed to compute the string `s`, and return a reference to it. Since
122 the contract of our function says the reference must outlive `'a`, that's the
123 lifetime we infer for the reference. Unfortunately, `s` was defined in the
124 scope `'b`, so the only way this is sound is if `'b` contains `'a` -- which is
125 clearly false since `'a` must contain the function call itself. We have therefore
126 created a reference whose lifetime outlives its referent, which is *literally*
127 the first thing we said that references can't do. The compiler rightfully blows
130 To make this more clear, we can expand the example:
133 fn as_str<'a>(data: &'a u32) -> &'a str {
135 let s = format!("{}", data);
144 // An anonymous scope is introduced because the borrow does not
145 // need to last for the whole scope x is valid for. The return
146 // of as_str must find a str somewhere before this function
147 // call. Obviously not happening.
148 println!("{}", as_str::<'d>(&'d x));
156 Of course, the right way to write this function is as follows:
159 fn to_string(data: &u32) -> String {
164 We must produce an owned value inside the function to return it! The only way
165 we could have returned an `&'a str` would have been if it was in a field of the
166 `&'a u32`, which is obviously not the case.
168 (Actually we could have also just returned a string literal, which as a global
169 can be considered to reside at the bottom of the stack; though this limits
170 our implementation *just a bit*.)
176 # Example: aliasing a mutable reference
178 How about the other example:
181 let mut data = vec![1, 2, 3];
189 let mut data: Vec<i32> = vec![1, 2, 3];
191 // 'b is as big as we need this borrow to be
192 // (just need to get to `println!`)
193 let x: &'b i32 = Index::index::<'b>(&'b data, 0);
195 // Temporary scope because we don't need the
196 // &mut to last any longer.
197 Vec::push(&'c mut data, 4);
204 The problem here is a bit more subtle and interesting. We want Rust to
205 reject this program for the following reason: We have a live shared reference `x`
206 to a descendant of `data` when we try to take a mutable reference to `data`
207 to `push`. This would create an aliased mutable reference, which would
208 violate the *second* rule of references.
210 However this is *not at all* how Rust reasons that this program is bad. Rust
211 doesn't understand that `x` is a reference to a subpath of `data`. It doesn't
212 understand `Vec` at all. What it *does* see is that `x` has to live for `'b` to
213 be printed. The signature of `Index::index` subsequently demands that the
214 reference we take to `data` has to survive for `'b`. When we try to call `push`,
215 it then sees us try to make an `&'c mut data`. Rust knows that `'c` is contained
216 within `'b`, and rejects our program because the `&'b data` must still be live!
218 Here we see that the lifetime system is much more coarse than the reference
219 semantics we're actually interested in preserving. For the most part, *that's
220 totally ok*, because it keeps us from spending all day explaining our program
221 to the compiler. However it does mean that several programs that are totally
222 correct with respect to Rust's *true* semantics are rejected because lifetimes
227 # The area covered by a lifetime
229 The lifetime (sometimes called a *borrow*) is *alive* from the place it is
230 created to its last use. The borrowed thing needs to outlive only borrows that
231 are alive. This looks simple, but there are few subtleties.
233 The following snippet compiles, because after printing `x`, it is no longer
234 needed, so it doesn't matter if it is dangling or aliased (even though the
235 variable `x` *technically* exists to the very end of the scope).
238 let mut data = vec![1, 2, 3];
241 // This is OK, x is no longer needed
245 However, if the value has a destructor, the destructor is run at the end of the
246 scope. And running the destructor is considered a use ‒ obviously the last one.
247 So, this will *not* compile.
249 ```rust,edition2018,compile_fail
251 struct X<'a>(&'a i32);
253 impl Drop for X<'_> {
254 fn drop(&mut self) {}
257 let mut data = vec![1, 2, 3];
261 // Here, the destructor is run and therefore this'll fail to compile.
264 Furthermore, there might be multiple possible last uses of the borrow, for
265 example in each branch of a condition.
268 # fn some_condition() -> bool { true }
269 let mut data = vec![1, 2, 3];
272 if some_condition() {
273 println!("{}", x); // This is the last use of `x` in this branch
274 data.push(4); // So we can push here
276 // There's no use of `x` in here, so effectively the last use is the
277 // creation of x at the top of the example.
282 And a lifetime can have a pause in it. Or you might look at it as two distinct
283 borrows just being tied to the same local variable. This often happens around
284 loops (writing a new value of a variable at the end of the loop and using it for
285 the last time at the top of the next iteration).
288 let mut data = vec![1, 2, 3];
289 // This mut allows us to change where the reference points to
290 let mut x = &data[0];
292 println!("{}", x); // Last use of this borrow
294 x = &data[3]; // We start a new borrow here
298 Historically, Rust kept the borrow alive until the end of scope, so these
299 examples might fail to compile with older compilers. Also, there are still some
300 corner cases where Rust fails to properly shorten the live part of the borrow
301 and fails to compile even when it looks like it should. These'll be solved over