|
1 | 1 | % Lifetimes
|
2 | 2 |
|
3 |
| -Coming Soon! Until then, check out the [ownership](ownership.html) chapter. |
| 3 | +This guide is one of three presenting Rust’s ownership system. This is one of |
| 4 | +Rust’s most unique and compelling features, with which Rust developers should |
| 5 | +become quite acquainted. Ownership is how Rust achieves its largest goal, |
| 6 | +memory safety. The there are a few distinct concepts, each with its own |
| 7 | +chapter: |
| 8 | + |
| 9 | +* [ownership][ownership], ownership, the key concept |
| 10 | +* [borrowing][borrowing], and their associated feature ‘references’ |
| 11 | +* lifetimes, which you’re reading now |
| 12 | + |
| 13 | +These three chapters are related, and in order. You’ll need all three to fully |
| 14 | +understand the ownership system. |
| 15 | + |
| 16 | +[ownership]: ownership.html |
| 17 | +[borrowing]: references-and-borrowing.html |
| 18 | + |
| 19 | +# Meta |
| 20 | + |
| 21 | +Before we get to the details, two important notes about the ownership system. |
| 22 | + |
| 23 | +Rust has a focus on safety and speed. It accomplishes these goals through many |
| 24 | +‘zero-cost abstractions’, which means that in Rust, abstractions cost as little |
| 25 | +as possible in order to make them work. The ownership system is a prime example |
| 26 | +of a zero cost abstraction. All of the analysis we’ll talk about in this guide |
| 27 | +is _done at compile time_. You do not pay any run-time cost for any of these |
| 28 | +features. |
| 29 | + |
| 30 | +However, this system does have a certain cost: learning curve. Many new users |
| 31 | +to Rust experience something we like to call ‘fighting with the borrow |
| 32 | +checker’, where the Rust compiler refuses to compile a program that the author |
| 33 | +thinks is valid. This often happens because the programmer’s mental model of |
| 34 | +how ownership should work doesn’t match the actual rules that Rust implements. |
| 35 | +You probably will experience similar things at first. There is good news, |
| 36 | +however: more experienced Rust developers report that once they work with the |
| 37 | +rules of the ownership system for a period of time, they fight the borrow |
| 38 | +checker less and less. |
| 39 | + |
| 40 | +With that in mind, let’s learn about lifetimes. |
| 41 | + |
| 42 | +# Lifetimes |
| 43 | + |
| 44 | +Lending out a reference to a resource that someone else owns can be |
| 45 | +complicated, however. For example, imagine this set of operations: |
| 46 | + |
| 47 | +- I acquire a handle to some kind of resource. |
| 48 | +- I lend you a reference to the resource. |
| 49 | +- I decide I’m done with the resource, and deallocate it, while you still have |
| 50 | + your reference. |
| 51 | +- You decide to use the resource. |
| 52 | + |
| 53 | +Uh oh! Your reference is pointing to an invalid resource. This is called a |
| 54 | +dangling pointer or ‘use after free’, when the resource is memory. |
| 55 | + |
| 56 | +To fix this, we have to make sure that step four never happens after step |
| 57 | +three. The ownership system in Rust does this through a concept called |
| 58 | +lifetimes, which describe the scope that a reference is valid for. |
| 59 | + |
| 60 | +When we have a function that takes a reference by argument, we can be implicit |
| 61 | +or explicit about the lifetime of the reference: |
| 62 | + |
| 63 | +```rust |
| 64 | +// implicit |
| 65 | +fn foo(x: &i32) { |
| 66 | +} |
| 67 | + |
| 68 | +// explicit |
| 69 | +fn bar<'a>(x: &'a i32) { |
| 70 | +} |
| 71 | +``` |
| 72 | + |
| 73 | +The `'a` reads ‘the lifetime a’. Technically, every reference has some `'foo` |
| 74 | +associated with it, but the compiler lets you elide them in common cases. |
| 75 | +Before we get to that, though, let’s break the explicit example down: |
| 76 | + |
| 77 | +```rust,ignore |
| 78 | +fn bar<'a>(...) |
| 79 | +``` |
| 80 | + |
| 81 | +This part declares our lifetimes. This says that `foo` has one lifetime, `'a`. |
| 82 | +If we had two parameters, it would look like this: |
| 83 | + |
| 84 | +```rust,ignore |
| 85 | +fn bar<'a, 'b>(...) |
| 86 | +``` |
| 87 | + |
| 88 | +Then in our parameter list, we use the lifetimes we’ve named: |
| 89 | + |
| 90 | +```rust,ignore |
| 91 | +...(x: &'a i32) |
| 92 | +``` |
| 93 | + |
| 94 | +If we wanted an `&mut` reference, we’d do this: |
| 95 | + |
| 96 | +```rust,ignore |
| 97 | +...(x: &' i32) |
| 98 | +``` |
| 99 | + |
| 100 | +If you compare `&mut i32` to `&'a mut i32`, they’re the same, it’s just that |
| 101 | +the lifetime `'a` has snuck in between the `&` and the `mut i32`. We read `&mut |
| 102 | +i32` as ‘a mutable reference to an i32’ and `&'a mut i32` as ‘a mutable |
| 103 | +reference to an `i32` with the lifetime `'a`’. |
| 104 | + |
| 105 | +You’ll also need explicit lifetimes when working with [`struct`][structs]s: |
| 106 | + |
| 107 | +```rust |
| 108 | +struct Foo<'a> { |
| 109 | + x: &'a i32, |
| 110 | +} |
| 111 | + |
| 112 | +fn main() { |
| 113 | + let y = &5; // this is the same as `let _y = 5; let y = &_y;` |
| 114 | + let f = Foo { x: y }; |
| 115 | + |
| 116 | + println!("{}", f.x); |
| 117 | +} |
| 118 | +``` |
| 119 | + |
| 120 | +[struct]: structs.html |
| 121 | + |
| 122 | +As you can see, `struct`s can also have lifetimes. In a similar way to functions, |
| 123 | + |
| 124 | +```rust |
| 125 | +struct Foo<'a> { |
| 126 | +# x: &'a i32, |
| 127 | +# } |
| 128 | +``` |
| 129 | + |
| 130 | +declares a lifetime, and |
| 131 | + |
| 132 | +```rust |
| 133 | +# struct Foo<'a> { |
| 134 | +x: &'a i32, |
| 135 | +# } |
| 136 | +``` |
| 137 | + |
| 138 | +uses it. So why do we need a lifetime here? We need to ensure that any reference |
| 139 | +to a `Foo` cannot outlive the reference to an `i32` it contains. |
| 140 | + |
| 141 | +## Thinking in scopes |
| 142 | + |
| 143 | +A way to think about lifetimes is to visualize the scope that a reference is |
| 144 | +valid for. For example: |
| 145 | + |
| 146 | +```rust |
| 147 | +fn main() { |
| 148 | + let y = &5; // -+ y goes into scope |
| 149 | + // | |
| 150 | + // stuff // | |
| 151 | + // | |
| 152 | +} // -+ y goes out of scope |
| 153 | +``` |
| 154 | + |
| 155 | +Adding in our `Foo`: |
| 156 | + |
| 157 | +```rust |
| 158 | +struct Foo<'a> { |
| 159 | + x: &'a i32, |
| 160 | +} |
| 161 | + |
| 162 | +fn main() { |
| 163 | + let y = &5; // -+ y goes into scope |
| 164 | + let f = Foo { x: y }; // -+ f goes into scope |
| 165 | + // stuff // | |
| 166 | + // | |
| 167 | +} // -+ f and y go out of scope |
| 168 | +``` |
| 169 | + |
| 170 | +Our `f` lives within the scope of `y`, so everything works. What if it didn’t? |
| 171 | +This code won’t work: |
| 172 | + |
| 173 | +```rust,ignore |
| 174 | +struct Foo<'a> { |
| 175 | + x: &'a i32, |
| 176 | +} |
| 177 | +
|
| 178 | +fn main() { |
| 179 | + let x; // -+ x goes into scope |
| 180 | + // | |
| 181 | + { // | |
| 182 | + let y = &5; // ---+ y goes into scope |
| 183 | + let f = Foo { x: y }; // ---+ f goes into scope |
| 184 | + x = &f.x; // | | error here |
| 185 | + } // ---+ f and y go out of scope |
| 186 | + // | |
| 187 | + println!("{}", x); // | |
| 188 | +} // -+ x goes out of scope |
| 189 | +``` |
| 190 | + |
| 191 | +Whew! As you can see here, the scopes of `f` and `y` are smaller than the scope |
| 192 | +of `x`. But when we do `x = &f.x`, we make `x` a reference to something that’s |
| 193 | +about to go out of scope. |
| 194 | + |
| 195 | +Named lifetimes are a way of giving these scopes a name. Giving something a |
| 196 | +name is the first step towards being able to talk about it. |
| 197 | + |
| 198 | +## 'static |
| 199 | + |
| 200 | +The lifetime named ‘static’ is a special lifetime. It signals that something |
| 201 | +has the lifetime of the entire program. Most Rust programmers first come across |
| 202 | +`'static` when dealing with strings: |
| 203 | + |
| 204 | +```rust |
| 205 | +let x: &'static str = "Hello, world."; |
| 206 | +``` |
| 207 | + |
| 208 | +String literals have the type `&'static str` because the reference is always |
| 209 | +alive: they are baked into the data segment of the final binary. Another |
| 210 | +example are globals: |
| 211 | + |
| 212 | +```rust |
| 213 | +static FOO: i32 = 5; |
| 214 | +let x: &'static i32 = &FOO; |
| 215 | +``` |
| 216 | + |
| 217 | +This adds an `i32` to the data segment of the binary, and `x` is a reference |
| 218 | +to it. |
| 219 | + |
| 220 | +## Lifetime Elision |
| 221 | + |
| 222 | +Rust supports powerful local type inference in function bodies, but it’s |
| 223 | +forbidden in item signatures to allow reasoning about the types just based in |
| 224 | +the item signature alone. However, for ergonomic reasons a very restricted |
| 225 | +secondary inference algorithm called “lifetime elision” applies in function |
| 226 | +signatures. It infers only based on the signature components themselves and not |
| 227 | +based on the body of the function, only infers lifetime parameters, and does |
| 228 | +this with only three easily memorizable and unambiguous rules. This makes |
| 229 | +lifetime elision a shorthand for writing an item signature, while not hiding |
| 230 | +away the actual types involved as full local inference would if applied to it. |
| 231 | + |
| 232 | +When talking about lifetime elision, we use the term *input lifetime* and |
| 233 | +*output lifetime*. An *input lifetime* is a lifetime associated with a parameter |
| 234 | +of a function, and an *output lifetime* is a lifetime associated with the return |
| 235 | +value of a function. For example, this function has an input lifetime: |
| 236 | + |
| 237 | +```rust,ignore |
| 238 | +fn foo<'a>(bar: &'a str) |
| 239 | +``` |
| 240 | + |
| 241 | +This one has an output lifetime: |
| 242 | + |
| 243 | +```rust,ignore |
| 244 | +fn foo<'a>() -> &'a str |
| 245 | +``` |
| 246 | + |
| 247 | +This one has a lifetime in both positions: |
| 248 | + |
| 249 | +```rust,ignore |
| 250 | +fn foo<'a>(bar: &'a str) -> &'a str |
| 251 | +``` |
| 252 | + |
| 253 | +Here are the three rules: |
| 254 | + |
| 255 | +* Each elided lifetime in a function’s arguments becomes a distinct lifetime |
| 256 | + parameter. |
| 257 | + |
| 258 | +* If there is exactly one input lifetime, elided or not, that lifetime is |
| 259 | + assigned to all elided lifetimes in the return values of that function. |
| 260 | + |
| 261 | +* If there are multiple input lifetimes, but one of them is `&self` or `&mut |
| 262 | + self`, the lifetime of `self` is assigned to all elided output lifetimes. |
| 263 | + |
| 264 | +Otherwise, it is an error to elide an output lifetime. |
| 265 | + |
| 266 | +### Examples |
| 267 | + |
| 268 | +Here are some examples of functions with elided lifetimes. We’ve paired each |
| 269 | +example of an elided lifetime with its expanded form. |
| 270 | + |
| 271 | +```rust,ignore |
| 272 | +fn print(s: &str); // elided |
| 273 | +fn print<'a>(s: &'a str); // expanded |
| 274 | +
|
| 275 | +fn debug(lvl: u32, s: &str); // elided |
| 276 | +fn debug<'a>(lvl: u32, s: &'a str); // expanded |
| 277 | +
|
| 278 | +// In the preceding example, `lvl` doesn’t need a lifetime because it’s not a |
| 279 | +// reference (`&`). Only things relating to references (such as a `struct` |
| 280 | +// which contains a reference) need lifetimes. |
| 281 | +
|
| 282 | +fn substr(s: &str, until: u32) -> &str; // elided |
| 283 | +fn substr<'a>(s: &'a str, until: u32) -> &'a str; // expanded |
| 284 | +
|
| 285 | +fn get_str() -> &str; // ILLEGAL, no inputs |
| 286 | +
|
| 287 | +fn frob(s: &str, t: &str) -> &str; // ILLEGAL, two inputs |
| 288 | +fn frob<'a, 'b>(s: &'a str, t: &'b str) -> &str; // Expanded: Output lifetime is unclear |
| 289 | +
|
| 290 | +fn get_mut(&mut self) -> &mut T; // elided |
| 291 | +fn get_mut<'a>(&'a mut self) -> &'a mut T; // expanded |
| 292 | +
|
| 293 | +fn args<T:ToCStr>(&mut self, args: &[T]) -> &mut Command // elided |
| 294 | +fn args<'a, 'b, T:ToCStr>(&'a mut self, args: &'b [T]) -> &'a mut Command // expanded |
| 295 | +
|
| 296 | +fn new(buf: &mut [u8]) -> BufWriter; // elided |
| 297 | +fn new<'a>(buf: &'a mut [u8]) -> BufWriter<'a> // expanded |
| 298 | +``` |
0 commit comments