-
Notifications
You must be signed in to change notification settings - Fork 492
/
destructors.md
407 lines (335 loc) · 14.7 KB
/
destructors.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
# Destructors
When an [initialized] [variable] or [temporary] goes out of
[scope](#drop-scopes), its *destructor* is run, or it is *dropped*. [Assignment]
also runs the destructor of its left-hand operand, if it's initialized. If a
variable has been partially initialized, only its initialized fields are
dropped.
The destructor of a type `T` consists of:
1. If `T: Drop`, calling [`<T as std::ops::Drop>::drop`]
2. Recursively running the destructor of all of its fields.
* The fields of a [struct] are dropped in declaration order.
* The fields of the active [enum variant] are dropped in declaration order.
* The fields of a [tuple] are dropped in order.
* The elements of an [array] or owned [slice] are dropped from the
first element to the last.
* The variables that a [closure] captures by move are dropped in an
unspecified order.
* [Trait objects] run the destructor of the underlying type.
* Other types don't result in any further drops.
If a destructor must be run manually, such as when implementing your own smart
pointer, [`std::ptr::drop_in_place`] can be used.
Some examples:
```rust
struct PrintOnDrop(&'static str);
impl Drop for PrintOnDrop {
fn drop(&mut self) {
println!("{}", self.0);
}
}
let mut overwritten = PrintOnDrop("drops when overwritten");
overwritten = PrintOnDrop("drops when scope ends");
let tuple = (PrintOnDrop("Tuple first"), PrintOnDrop("Tuple second"));
let moved;
// No destructor run on assignment.
moved = PrintOnDrop("Drops when moved");
// Drops now, but is then uninitialized.
moved;
// Uninitialized does not drop.
let uninitialized: PrintOnDrop;
// After a partial move, only the remaining fields are dropped.
let mut partial_move = (PrintOnDrop("first"), PrintOnDrop("forgotten"));
// Perform a partial move, leaving only `partial_move.0` initialized.
core::mem::forget(partial_move.1);
// When partial_move's scope ends, only the first field is dropped.
```
## Drop scopes
Each variable or temporary is associated to a *drop scope*. When control flow
leaves a drop scope all variables associated to that scope are dropped in
reverse order of declaration (for variables) or creation (for temporaries).
Drop scopes are determined after replacing [`for`], [`if let`], and
[`while let`] expressions with the equivalent expressions using [`match`].
Overloaded operators are not distinguished from built-in operators and [binding
modes] are not considered.
Given a function, or closure, there are drop scopes for:
* The entire function
* Each [statement]
* Each [expression]
* Each block, including the function body
* In the case of a [block expression], the scope for the block and the
expression are the same scope.
* Each arm of a `match` expression
Drop scopes are nested within one another as follows. When multiple scopes are
left at once, such as when returning from a function, variables are dropped
from the inside outwards.
* The entire function scope is the outer most scope.
* The function body block is contained within the scope of the entire function.
* The parent of the expression in an expression statement is the scope of the
statement.
* The parent of the initializer of a [`let` statement] is the `let` statement's
scope.
* The parent of a statement scope is the scope of the block that contains the
statement.
* The parent of the expression for a `match` guard is the scope of the arm that
the guard is for.
* The parent of the expression after the `=>` in a `match` expression is the
scope of the arm that it's in.
* The parent of the arm scope is the scope of the `match` expression that it
belongs to.
* The parent of all other scopes is the scope of the immediately enclosing
expression.
### Scopes of function parameters
All function parameters are in the scope of the entire function body, so are
dropped last when evaluating the function. Each actual function parameter is
dropped after any bindings introduced in that parameter's pattern.
```rust
# struct PrintOnDrop(&'static str);
# impl Drop for PrintOnDrop {
# fn drop(&mut self) {
# println!("drop({})", self.0);
# }
# }
// Drops `y`, then the second parameter, then `x`, then the first parameter
fn patterns_in_parameters(
(x, _): (PrintOnDrop, PrintOnDrop),
(_, y): (PrintOnDrop, PrintOnDrop),
) {}
// drop order is 3 2 0 1
patterns_in_parameters(
(PrintOnDrop("0"), PrintOnDrop("1")),
(PrintOnDrop("2"), PrintOnDrop("3")),
);
```
### Scopes of local variables
Local variables declared in a `let` statement are associated to the scope of
the block that contains the `let` statement. Local variables declared in a
`match` expression are associated to the arm scope of the `match` arm that they
are declared in.
```rust
# struct PrintOnDrop(&'static str);
# impl Drop for PrintOnDrop {
# fn drop(&mut self) {
# println!("drop({})", self.0);
# }
# }
let declared_first = PrintOnDrop("Dropped last in outer scope");
{
let declared_in_block = PrintOnDrop("Dropped in inner scope");
}
let declared_last = PrintOnDrop("Dropped first in outer scope");
```
If multiple patterns are used in the same arm for a `match` expression, then an
unspecified pattern will be used to determine the drop order.
### Temporary scopes
The *temporary scope* of an expression is the scope that is used for the
temporary variable that holds the result of that expression when used in a
[place context], unless it is [promoted].
Apart from lifetime extension, the temporary scope of an expression is the
smallest scope that contains the expression and is one of the following:
* The entire function.
* A statement.
* The body of an [`if`], [`while`] or [`loop`] expression.
* The `else` block of an `if` expression.
* The condition expression of an `if` or `while` expression, or a `match`
guard.
* The body expression for a match arm.
* The second operand of a [lazy boolean expression].
> **Notes**:
>
> Temporaries that are created in the final expression of a function
> body are dropped *after* any named variables bound in the function body.
> Their drop scope is the entire function, as there is no smaller enclosing temporary scope.
>
> The [scrutinee] of a `match` expression is not a temporary scope, so
> temporaries in the scrutinee can be dropped after the `match` expression. For
> example, the temporary for `1` in `match 1 { ref mut z => z };` lives until
> the end of the statement.
Some examples:
```rust
# struct PrintOnDrop(&'static str);
# impl Drop for PrintOnDrop {
# fn drop(&mut self) {
# println!("drop({})", self.0);
# }
# }
let local_var = PrintOnDrop("local var");
// Dropped once the condition has been evaluated
if PrintOnDrop("If condition").0 == "If condition" {
// Dropped at the end of the block
PrintOnDrop("If body").0
} else {
unreachable!()
};
// Dropped at the end of the statement
(PrintOnDrop("first operand").0 == ""
// Dropped at the )
|| PrintOnDrop("second operand").0 == "")
// Dropped at the end of the expression
|| PrintOnDrop("third operand").0 == "";
// Dropped at the end of the function, after local variables.
// Changing this to a statement containing a return expression would make the
// temporary be dropped before the local variables. Binding to a variable
// which is then returned would also make the temporary be dropped first.
match PrintOnDrop("Matched value in final expression") {
// Dropped once the condition has been evaluated
_ if PrintOnDrop("guard condition").0 == "" => (),
_ => (),
}
```
### Operands
Temporaries are also created to hold the result of operands to an expression
while the other operands are evaluated. The temporaries are associated to the
scope of the expression with that operand. Since the temporaries are moved from
once the expression is evaluated, dropping them has no effect unless one of the
operands to an expression breaks out of the expression, returns, or panics.
```rust
# struct PrintOnDrop(&'static str);
# impl Drop for PrintOnDrop {
# fn drop(&mut self) {
# println!("drop({})", self.0);
# }
# }
loop {
// Tuple expression doesn't finish evaluating so operands drop in reverse order
(
PrintOnDrop("Outer tuple first"),
PrintOnDrop("Outer tuple second"),
(
PrintOnDrop("Inner tuple first"),
PrintOnDrop("Inner tuple second"),
break,
),
PrintOnDrop("Never created"),
);
}
```
### Constant promotion
Promotion of a value expression to a `'static` slot occurs when the expression
could be written in a constant and borrowed, and that borrow could be dereferenced
where
the expression was originally written, without changing the runtime behavior.
That is, the promoted expression can be evaluated at compile-time and the
resulting value does not contain [interior mutability] or [destructors] (these
properties are determined based on the value where possible, e.g. `&None`
always has the type `&'static Option<_>`, as it contains nothing disallowed).
### Temporary lifetime extension
> **Note**: The exact rules for temporary lifetime extension are subject to
> change. This is describing the current behavior only.
The temporary scopes for expressions in `let` statements are sometimes
*extended* to the scope of the block containing the `let` statement. This is
done when the usual temporary scope would be too small, based on certain
syntactic rules. For example:
```rust
let x = &mut 0;
// Usually a temporary would be dropped by now, but the temporary for `0` lives
// to the end of the block.
println!("{}", x);
```
If a [borrow][borrow expression], [dereference][dereference expression],
[field][field expression], or [tuple indexing expression] has an extended
temporary scope then so does its operand. If an [indexing expression] has an
extended temporary scope then the indexed expression also has an extended
temporary scope.
#### Extending based on patterns
An *extending pattern* is either
* An [identifier pattern] that binds by reference or mutable reference.
* A [struct][struct pattern], [tuple][tuple pattern], [tuple struct][tuple
struct pattern], or [slice][slice pattern] pattern where at least one of the
direct subpatterns is an extending pattern.
So `ref x`, `V(ref x)` and `[ref x, y]` are all extending patterns, but `x`,
`&ref x` and `&(ref x,)` are not.
If the pattern in a `let` statement is an extending pattern then the temporary
scope of the initializer expression is extended.
#### Extending based on expressions
For a let statement with an initializer, an *extending expression* is an
expression which is one of the following:
* The initializer expression.
* The operand of an extending [borrow expression].
* The operand(s) of an extending [array][array expression], [cast][cast
expression], [braced struct][struct expression], or [tuple][tuple expression]
expression.
* The final expression of any extending [block expression].
So the borrow expressions in `&mut 0`, `(&1, &mut 2)`, and `Some { 0: &mut 3 }`
are all extending expressions. The borrows in `&0 + &1` and `Some(&mut 0)` are
not: the latter is syntactically a function call expression.
The operand of any extending borrow expression has its temporary scope
extended.
#### Examples
Here are some examples where expressions have extended temporary scopes:
```rust
# fn temp() {}
# trait Use { fn use_temp(&self) -> &Self { self } }
# impl Use for () {}
// The temporary that stores the result of `temp()` lives in the same scope
// as x in these cases.
let x = &temp();
let x = &temp() as &dyn Send;
let x = (&*&temp(),);
let x = { [Some { 0: &temp(), }] };
let ref x = temp();
let ref x = *&temp();
# x;
```
Here are some examples where expressions don't have extended temporary scopes:
```rust,compile_fail
# fn temp() {}
# trait Use { fn use_temp(&self) -> &Self { self } }
# impl Use for () {}
// The temporary that stores the result of `temp()` only lives until the
// end of the let statement in these cases.
let x = Some(&temp()); // ERROR
let x = (&temp()).use_temp(); // ERROR
# x;
```
## Not running destructors
[`std::mem::forget`] can be used to prevent the destructor of a variable from being run,
and [`std::mem::ManuallyDrop`] provides a wrapper to prevent a
variable or field from being dropped automatically.
> Note: Preventing a destructor from being run via [`std::mem::forget`] or other means is safe even if it has a type that isn't `'static`.
> Besides the places where destructors are guaranteed to run as defined by this document, types may *not* safely rely on a destructor being run for soundness.
[Assignment]: expressions/operator-expr.md#assignment-expressions
[binding modes]: patterns.md#binding-modes
[closure]: types/closure.md
[destructors]: destructors.md
[expression]: expressions.md
[identifier pattern]: patterns.md#identifier-patterns
[initialized]: glossary.md#initialized
[interior mutability]: interior-mutability.md
[lazy boolean expression]: expressions/operator-expr.md#lazy-boolean-operators
[place context]: expressions.md#place-expressions-and-value-expressions
[promoted]: destructors.md#constant-promotion
[scrutinee]: glossary.md#scrutinee
[statement]: statements.md
[temporary]: expressions.md#temporaries
[variable]: variables.md
[array]: types/array.md
[enum variant]: types/enum.md
[slice]: types/slice.md
[struct]: types/struct.md
[Trait objects]: types/trait-object.md
[tuple]: types/tuple.md
[slice pattern]: patterns.md#slice-patterns
[struct pattern]: patterns.md#struct-patterns
[tuple pattern]: patterns.md#tuple-patterns
[tuple struct pattern]: patterns.md#tuple-struct-patterns
[array expression]: expressions/array-expr.md#array-expressions
[block expression]: expressions/block-expr.md
[borrow expression]: expressions/operator-expr.md#borrow-operators
[cast expression]: expressions/operator-expr.md#type-cast-expressions
[dereference expression]: expressions/operator-expr.md#the-dereference-operator
[field expression]: expressions/field-expr.md
[indexing expression]: expressions/array-expr.md#array-and-slice-indexing-expressions
[struct expression]: expressions/struct-expr.md
[tuple expression]: expressions/tuple-expr.md#tuple-expressions
[tuple indexing expression]: expressions/tuple-expr.md#tuple-indexing-expressions
[`for`]: expressions/loop-expr.md#iterator-loops
[`if let`]: expressions/if-expr.md#if-let-expressions
[`if`]: expressions/if-expr.md#if-expressions
[`let` statement]: statements.md#let-statements
[`loop`]: expressions/loop-expr.md#infinite-loops
[`match`]: expressions/match-expr.md
[`while let`]: expressions/loop-expr.md#predicate-pattern-loops
[`while`]: expressions/loop-expr.md#predicate-loops
[`<T as std::ops::Drop>::drop`]: ../std/ops/trait.Drop.html#tymethod.drop
[`std::ptr::drop_in_place`]: ../std/ptr/fn.drop_in_place.html
[`std::mem::forget`]: ../std/mem/fn.forget.html
[`std::mem::ManuallyDrop`]: ../std/mem/struct.ManuallyDrop.html