1
1
% The Perils Of RAII
2
2
3
- Ownership Based Resource Management (AKA RAII: Resource Acquisition is Initialization) is
3
+ Ownership Based Resource Management (AKA RAII: Resource Acquisition Is Initialization) is
4
4
something you'll interact with a lot in Rust. Especially if you use the standard library.
5
5
6
6
Roughly speaking the pattern is as follows: to acquire a resource, you create an object that
@@ -38,10 +38,8 @@ treating the old copy as uninitialized -- a no-op.
38
38
39
39
While Rust provides a ` Default ` trait for specifying the moral equivalent of a default
40
40
constructor, it's incredibly rare for this trait to be used. This is because variables
41
- aren't implicitly initialized (see [ working with uninitialized memory] [ uninit ] for details).
42
- Default is basically only useful for generic programming.
43
-
44
- In concrete contexts, a type will provide a static ` new ` method for any
41
+ [ aren't implicitly initialized] [ uninit ] . Default is basically only useful for generic
42
+ programming. In concrete contexts, a type will provide a static ` new ` method for any
45
43
kind of "default" constructor. This has no relation to ` new ` in other
46
44
languages and has no special meaning. It's just a naming convention.
47
45
@@ -59,20 +57,16 @@ fn drop(&mut self);
59
57
```
60
58
61
59
This method gives the type time to somehow finish what it was doing. ** After ` drop ` is run,
62
- Rust will recursively try to drop all of the fields of the ` self ` struct ** . This is a
60
+ Rust will recursively try to drop all of the fields of ` self ` ** . This is a
63
61
convenience feature so that you don't have to write "destructor boilerplate" to drop
64
62
children. If a struct has no special logic for being dropped other than dropping its
65
63
children, then it means ` Drop ` doesn't need to be implemented at all!
66
64
67
- ** There is no way to prevent this behaviour in Rust 1.0** .
65
+ ** There is no stable way to prevent this behaviour in Rust 1.0** .
68
66
69
67
Note that taking ` &mut self ` means that even if you * could* suppress recursive Drop,
70
68
Rust will prevent you from e.g. moving fields out of self. For most types, this
71
- is totally fine:
72
-
73
- * They own all their data (they don't contain pointers to elsewhere).
74
- * There's no additional state passed into drop to try to send things.
75
- * ` self ` is about to be marked as uninitialized (and therefore inaccessible).
69
+ is totally fine.
76
70
77
71
For instance, a custom implementation of ` Box ` might write ` Drop ` like this:
78
72
@@ -120,7 +114,7 @@ impl<T> Drop for SuperBox<T> {
120
114
}
121
115
```
122
116
123
- because after we deallocate the ` box ` 's ptr in SuperBox's destructor, Rust will
117
+ After we deallocate the ` box ` 's ptr in SuperBox's destructor, Rust will
124
118
happily proceed to tell the box to Drop itself and everything will blow up with
125
119
use-after-frees and double-frees.
126
120
@@ -216,7 +210,7 @@ refers to it. The collection will sit around uselessly, holding on to its
216
210
precious resources until the program terminates (at which point all those
217
211
resources would have been reclaimed by the OS anyway).
218
212
219
- We may consider a more restricted form of leak: failing to free memory that
213
+ We may consider a more restricted form of leak: failing to drop a value that
220
214
is unreachable. Rust also doesn't prevent this. In fact Rust has a * function
221
215
for doing this* : ` mem::forget ` . This function consumes the value it is passed
222
216
* and then doesn't run its destructor* .
@@ -232,26 +226,26 @@ It is reasonable for safe code to assume that destructor leaks do not happen,
232
226
as any program that leaks destructors is probably wrong. However * unsafe* code
233
227
cannot rely on destructors to be run to be * safe* . For most types this doesn't
234
228
matter: if you leak the destructor then the type is * by definition* inaccessible,
235
- so it doesn't matter, right? e.g. if you leak a ` Box<u8> ` then you waste some
236
- memory but that's hardly going to violate memory-safety.
229
+ so it doesn't matter, right? For instance, if you leak a ` Box<u8> ` then you
230
+ waste some memory but that's hardly going to violate memory-safety.
237
231
238
232
However where we must be careful with destructor leaks are * proxy* types.
239
233
These are types which manage access to a distinct object, but don't actually
240
234
own it. Proxy objects are quite rare. Proxy objects you'll need to care about
241
- are even rarer. However we'll focus on two interesting examples in the
235
+ are even rarer. However we'll focus on three interesting examples in the
242
236
standard library:
243
237
244
238
* ` vec::Drain `
245
239
* ` Rc `
246
-
240
+ * ` thread::scoped::JoinGuard `
247
241
248
242
249
243
250
244
## Drain
251
245
252
246
` drain ` is a collections API that moves data out of the container without
253
247
consuming the container. This enables us to reuse the allocation of a ` Vec `
254
- after claiming ownership over all of its contents. drain produces an iterator
248
+ after claiming ownership over all of its contents. It produces an iterator
255
249
(Drain) that returns the contents of the Vec by-value.
256
250
257
251
Now, consider Drain in the middle of iteration: some values have been moved out,
@@ -376,7 +370,7 @@ in memory.
376
370
377
371
378
372
379
- ## thread::scoped
373
+ ## thread::scoped::JoinGuard
380
374
381
375
The thread::scoped API intends to allow threads to be spawned that reference
382
376
data on the stack without any synchronization over that data. Usage looked like:
0 commit comments