]> git.proxmox.com Git - rustc.git/blame - src/doc/rustc-guide/src/closure.md
New upstream version 1.41.1+dfsg1
[rustc.git] / src / doc / rustc-guide / src / closure.md
CommitLineData
dc9dc135
XL
1# Closure Expansion in rustc
2
3This section describes how rustc handles closures. Closures in Rust are
4effectively "desugared" into structs that contain the values they use (or
5references to the values they use) from their creator's stack frame. rustc has
6the job of figuring out which values a closure uses and how, so it can decide
7whether to capture a given variable by shared reference, mutable reference, or
8by move. rustc also has to figure out which the closure traits ([`Fn`][fn],
9[`FnMut`][fn_mut], or [`FnOnce`][fn_once]) a closure is capable of
10implementing.
11
12[fn]: https://doc.rust-lang.org/std/ops/trait.Fn.html
13[fn_mut]:https://doc.rust-lang.org/std/ops/trait.FnMut.html
14[fn_once]: https://doc.rust-lang.org/std/ops/trait.FnOnce.html
15
16Let's start with a few examples:
17
18### Example 1
19
20To start, let's take a look at how the closure in the following example is desugared:
21
22```rust
23fn closure(f: impl Fn()) {
24 f();
25}
26
27fn main() {
28 let x: i32 = 10;
29 closure(|| println!("Hi {}", x)); // The closure just reads x.
30 println!("Value of x after return {}", x);
31}
32```
33
34Let's say the above is the content of a file called `immut.rs`. If we compile
35`immut.rs` using the following command. The [`-Zdump-mir=all`][dump-mir] flag will cause
36`rustc` to generate and dump the [MIR][mir] to a directory called `mir_dump`.
37```console
38> rustc +stage1 immut.rs -Zdump-mir=all
39```
40
41[mir]: ./mir/index.md
42[dump-mir]: ./mir/passes.md
43
44After we run this command, we will see a newly generated directory in our
45current working directory called `mir_dump`, which will contain several files.
46If we look at file `rustc.main.-------.mir_map.0.mir`, we will find, among
47other things, it also contains this line:
48
49```rust,ignore
50_4 = &_1;
51_3 = [closure@immut.rs:7:13: 7:36] { x: move _4 };
52```
53
54Note that in the MIR examples in this chapter, `_1` is `x`.
55
56Here in first line `_4 = &_1;`, the `mir_dump` tells us that `x` was borrowed
57as an immutable reference. This is what we would hope as our closure just
58reads `x`.
59
60### Example 2
61
62Here is another example:
63
64```rust
65fn closure(mut f: impl FnMut()) {
66 f();
67}
68
69fn main() {
70 let mut x: i32 = 10;
71 closure(|| {
72 x += 10; // The closure mutates the value of x
73 println!("Hi {}", x)
74 });
75 println!("Value of x after return {}", x);
76}
77```
78
79```rust,ignore
80_4 = &mut _1;
81_3 = [closure@mut.rs:7:13: 10:6] { x: move _4 };
82```
83This time along, in the line `_4 = &mut _1;`, we see that the borrow is changed to mutable borrow.
84Fair enough! The closure increments `x` by 10.
85
86### Example 3
87
88One more example:
89
90```rust
91fn closure(f: impl FnOnce()) {
92 f();
93}
94
95fn main() {
96 let x = vec![21];
97 closure(|| {
98 drop(x); // Makes x unusable after the fact.
99 });
100 // println!("Value of x after return {:?}", x);
101}
102```
103
104```rust,ignore
105_6 = [closure@move.rs:7:13: 9:6] { x: move _1 }; // bb16[3]: scope 1 at move.rs:7:13: 9:6
106```
107Here, `x` is directly moved into the closure and the access to it will not be permitted after the
108closure.
109
110## Inferences in the compiler
111
112Now let's dive into rustc code and see how all these inferences are done by the compiler.
113
114Let's start with defining a term that we will be using quite a bit in the rest of the discussion -
115*upvar*. An **upvar** is a variable that is local to the function where the closure is defined. So,
116in the above examples, **x** will be an upvar to the closure. They are also sometimes referred to as
117the *free variables* meaning they are not bound to the context of the closure.
118[`src/librustc/ty/query/mod.rs`][upvars] defines a query called *upvars* for this purpose.
119
120[upvars]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/query/queries/struct.upvars.html
121
122Other than lazy invocation, one other thing that the distinguishes a closure from a
123normal function is that it can use the upvars. It borrows these upvars from its surrounding
416331ca 124context; therefore the compiler has to determine the upvar's borrow type. The compiler starts with
dc9dc135
XL
125assigning an immutable borrow type and lowers the restriction (that is, changes it from
126**immutable** to **mutable** to **move**) as needed, based on the usage. In the Example 1 above, the
127closure only uses the variable for printing but does not modify it in any way and therefore, in the
128`mir_dump`, we find the borrow type for the upvar `x` to be immutable. In example 2, however, the
129closure modifies `x` and increments it by some value. Because of this mutation, the compiler, which
130started off assigning `x` as an immutable reference type, has to adjust it as a mutable reference.
131Likewise in the third example, the closure drops the vector and therefore this requires the variable
132`x` to be moved into the closure. Depending on the borrow kind, the closure has to implement the
133appropriate trait: `Fn` trait for immutable borrow, `FnMut` for mutable borrow,
134and `FnOnce` for move semantics.
135
136Most of the code related to the closure is in the
137[`src/librustc_typeck/check/upvar.rs`][upvar] file and the data structures are
138declared in the file [`src/librustc/ty/mod.rs`][ty].
139
140[upvar]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/check/upvar/index.html
141[ty]:https://doc.rust-lang.org/nightly/nightly-rustc/rustc/ty/index.html
142
416331ca 143Before we go any further, let's discuss how we can examine the flow of control through the rustc
dc9dc135
XL
144codebase. For closures specifically, set the `RUST_LOG` env variable as below and collect the
145output in a file:
146
147```console
148> RUST_LOG=rustc_typeck::check::upvar rustc +stage1 -Zdump-mir=all \
149 <.rs file to compile> 2> <file where the output will be dumped>
150```
151
152This uses the stage1 compiler and enables `debug!` logging for the
153`rustc_typeck::check::upvar` module.
154
155The other option is to step through the code using lldb or gdb.
156
1571. `rust-lldb build/x86_64-apple-darwin/stage1/bin/rustc test.rs`
1582. In lldb:
159 1. `b upvar.rs:134` // Setting the breakpoint on a certain line in the upvar.rs file`
160 2. `r` // Run the program until it hits the breakpoint
161
162Let's start with [`upvar.rs`][upvar]. This file has something called
60c5eb7d 163the [`euv::ExprUseVisitor`] which walks the source of the closure and
dc9dc135
XL
164invokes a callbackfor each upvar that is borrowed, mutated, or moved.
165
60c5eb7d 166[`euv::ExprUseVisitor`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/expr_use_visitor/struct.ExprUseVisitor.html
dc9dc135
XL
167
168```rust
169fn main() {
170 let mut x = vec![21];
171 let _cl = || {
172 let y = x[0]; // 1.
173 x[0] += 1; // 2.
174 };
175}
176```
177
178In the above example, our visitor will be called twice, for the lines marked 1 and 2, once for a
179shared borrow and another one for a mutable borrow. It will also tell us what was borrowed.
180
60c5eb7d 181The callbacks are defined by implementing the [`Delegate`] trait. The
dc9dc135
XL
182[`InferBorrowKind`][ibk] type implements `Delegate` and keeps a map that
183records for each upvar which mode of borrow was required. The modes of borrow
184can be `ByValue` (moved) or `ByRef` (borrowed). For `ByRef` borrows, it can be
185`shared`, `shallow`, `unique` or `mut` as defined in the
186[`src/librustc/mir/mod.rs`][mir_mod].
187
188[mir_mod]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc/mir/index.html
189
190`Delegate` defines a few different methods (the different callbacks):
191**consume**: for *move* of a variable, **borrow** for a *borrow* of some kind
192(shared or mutable), and **mutate** when we see an *assignment* of something.
193
194All of these callbacks have a common argument *cmt* which stands for Category,
195Mutability and Type and is defined in
196[`src/librustc/middle/mem_categorization.rs`][cmt]. Borrowing from the code
197comments, "`cmt` is a complete categorization of a value indicating where it
198originated and how it is located, as well as the mutability of the memory in
199which the value is stored". Based on the callback (consume, borrow etc.), we
200will call the relevant *adjust_upvar_borrow_kind_for_<something>* and pass the
201`cmt` along. Once the borrow type is adjusted, we store it in the table, which
202basically says what borrows were made for each closure.
203
204```rust,ignore
205self.tables
206 .borrow_mut()
207 .upvar_capture_map
208 .extend(delegate.adjust_upvar_captures);
209```
210
60c5eb7d 211[`Delegate`]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/expr_use_visitor/trait.Delegate.html
dc9dc135 212[ibk]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/check/upvar/struct.InferBorrowKind.html
60c5eb7d 213[cmt]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_typeck/mem_categorization/index.html