Skip to content

Commit 17fbdda

Browse files
committed
TRPL: ownership, borrowing, and lifetimes
Also, as @huonw guessed, move semantics really _does_ make more sense as a sub-chapter of ownership.
1 parent f191f92 commit 17fbdda

File tree

5 files changed

+741
-567
lines changed

5 files changed

+741
-567
lines changed

src/doc/trpl/SUMMARY.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
* [References and Borrowing](references-and-borrowing.md)
2727
* [Lifetimes](lifetimes.md)
2828
* [Mutability](mutability.md)
29-
* [Move semantics](move-semantics.md)
3029
* [Enums](enums.md)
3130
* [Match](match.md)
3231
* [Structs](structs.md)

src/doc/trpl/lifetimes.md

Lines changed: 296 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,298 @@
11
% Lifetimes
22

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

Comments
 (0)