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