Skip to content

Commit 48a1e99

Browse files
committed
---
yaml --- r: 222711 b: refs/heads/tmp c: 10af239 h: refs/heads/master i: 222709: 3740adb 222707: b4b8d54 222703: 50bcb8d v: v3
1 parent bd80652 commit 48a1e99

File tree

3 files changed

+97
-21
lines changed

3 files changed

+97
-21
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ refs/tags/0.11.0: e1247cb1d0d681be034adb4b558b5a0c0d5720f9
2525
refs/tags/0.12.0: f0c419429ef30723ceaf6b42f9b5a2aeb5d2e2d1
2626
refs/heads/beta: 2b45108ecb944d63daba0f1b5529ac4c8afdc295
2727
refs/tags/1.0.0-alpha: e42bd6d93a1d3433c486200587f8f9e12590a4d7
28-
refs/heads/tmp: 9c6a46b0eee9f991a7ae6e72164550f781654c66
28+
refs/heads/tmp: 10af239aad8024f1eed093947b8516ecf873d735
2929
refs/tags/1.0.0-alpha.2: 4c705f6bc559886632d3871b04f58aab093bfa2f
3030
refs/tags/homu-tmp: e58601ab085591c71a27ae82137fc313222c2270
3131
refs/tags/1.0.0-beta: 8cbb92b53468ee2b0c2d3eeb8567005953d40828

branches/tmp/raii.md

Lines changed: 14 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
% The Perils Of RAII
22

3-
Ownership Based Resource Management (AKA RAII: Resource Acquisition is Initialization) is
3+
Ownership Based Resource Management (AKA RAII: Resource Acquisition Is Initialization) is
44
something you'll interact with a lot in Rust. Especially if you use the standard library.
55

66
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.
3838

3939
While Rust provides a `Default` trait for specifying the moral equivalent of a default
4040
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
4543
kind of "default" constructor. This has no relation to `new` in other
4644
languages and has no special meaning. It's just a naming convention.
4745

@@ -59,20 +57,16 @@ fn drop(&mut self);
5957
```
6058

6159
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
6361
convenience feature so that you don't have to write "destructor boilerplate" to drop
6462
children. If a struct has no special logic for being dropped other than dropping its
6563
children, then it means `Drop` doesn't need to be implemented at all!
6664

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**.
6866

6967
Note that taking `&mut self` means that even if you *could* suppress recursive Drop,
7068
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.
7670

7771
For instance, a custom implementation of `Box` might write `Drop` like this:
7872

@@ -120,7 +114,7 @@ impl<T> Drop for SuperBox<T> {
120114
}
121115
```
122116

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
124118
happily proceed to tell the box to Drop itself and everything will blow up with
125119
use-after-frees and double-frees.
126120

@@ -216,7 +210,7 @@ refers to it. The collection will sit around uselessly, holding on to its
216210
precious resources until the program terminates (at which point all those
217211
resources would have been reclaimed by the OS anyway).
218212

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
220214
is unreachable. Rust also doesn't prevent this. In fact Rust has a *function
221215
for doing this*: `mem::forget`. This function consumes the value it is passed
222216
*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,
232226
as any program that leaks destructors is probably wrong. However *unsafe* code
233227
cannot rely on destructors to be run to be *safe*. For most types this doesn't
234228
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.
237231

238232
However where we must be careful with destructor leaks are *proxy* types.
239233
These are types which manage access to a distinct object, but don't actually
240234
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
242236
standard library:
243237

244238
* `vec::Drain`
245239
* `Rc`
246-
240+
* `thread::scoped::JoinGuard`
247241

248242

249243

250244
## Drain
251245

252246
`drain` is a collections API that moves data out of the container without
253247
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
255249
(Drain) that returns the contents of the Vec by-value.
256250

257251
Now, consider Drain in the middle of iteration: some values have been moved out,
@@ -376,7 +370,7 @@ in memory.
376370

377371

378372

379-
## thread::scoped
373+
## thread::scoped::JoinGuard
380374

381375
The thread::scoped API intends to allow threads to be spawned that reference
382376
data on the stack without any synchronization over that data. Usage looked like:

branches/tmp/unwinding.md

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
% Unwinding
2+
3+
Rust has a *tiered* error-handling scheme:
4+
5+
* If something might reasonably be absent, Option is used
6+
* If something goes wrong and can reasonably be handled, Result is used
7+
* If something goes wrong and cannot reasonably be handled, the thread panics
8+
* If something catastrophic happens, the program aborts
9+
10+
Option and Result are overwhelmingly preferred in most situations, especially
11+
since they can be promoted into a panic or abort at the API user's discretion.
12+
However, anything and everything *can* panic, and you need to be ready for this.
13+
Panics cause the thread to halt normal execution and unwind its stack, calling
14+
destructors as if every function instantly returned.
15+
16+
As of 1.0, Rust is of two minds when it comes to panics. In the long-long-ago,
17+
Rust was much more like Erlang. Like Erlang, Rust had lightweight tasks,
18+
and tasks were intended to kill themselves with a panic when they reached an
19+
untenable state. Unlike an exception in Java or C++, a panic could not be
20+
caught at any time. Panics could only be caught by the owner of the task, at which
21+
point they had to be handled or *that* task would itself panic.
22+
23+
Unwinding was important to this story because if a task's
24+
destructors weren't called, it would cause memory and other system resources to
25+
leak. Since tasks were expected to die during normal execution, this would make
26+
Rust very poor for long-running systems!
27+
28+
As the Rust we know today came to be, this style of programming grew out of
29+
fashion in the push for less-and-less abstraction. Light-weight tasks were
30+
killed in the name of heavy-weight OS threads. Still, panics could only be
31+
caught by the parent thread. This meant catching a panic required spinning up
32+
an entire OS thread! Although Rust maintains the philosophy that panics should
33+
not be used for "basic" error-handling like C++ or Java, it is still desirable
34+
to not have the entire program crash in the face of a panic.
35+
36+
In the near future there will be a stable interface for catching panics in an
37+
arbitrary location, though we would encourage you to still only do this
38+
sparingly. In particular, Rust's current unwinding implementation is heavily
39+
optimized for the "doesn't unwind" case. If a program doesn't unwind, there
40+
should be no runtime cost for the program being *ready* to unwind. As a
41+
consequence, *actually* unwinding will be more expensive than in e.g. Java.
42+
Don't build your programs to unwind under normal circumstances. Ideally, you
43+
should only panic for programming errors.
44+
45+
46+
47+
48+
# Exception Safety
49+
50+
Being ready for unwinding is often referred to as "exception safety"
51+
in the broader programming world. In Rust, their are two levels of exception
52+
safety that one may concern themselves with:
53+
54+
* In unsafe code, we *must* be exception safe to the point of not violating
55+
memory safety.
56+
57+
* In safe code, it is *good* to be exception safe to the point of your program
58+
doing the right thing.
59+
60+
As is the case in many places in Rust, unsafe code must be ready to deal with
61+
bad safe code, and that includes code that panics. Code that transiently creates
62+
unsound states must be careful that a panic does not cause that state to be
63+
used. Generally this means ensuring that only non-panicing code is run while
64+
these states exist, or making a guard that cleans up the state in the case of
65+
a panic. This does not necessarily mean that the state a panic witnesses is a
66+
fully *coherent* state. We need only guarantee that it's a *safe* state.
67+
68+
For instance, consider extending a Vec:
69+
70+
```rust
71+
72+
impl Extend<T> for Vec<T> {
73+
fn extend<I: IntoIter<Item=T>>(&mut self, iterable: I) {
74+
let mut iter = iterable.into_iter();
75+
let size_hint = iter.size_hint().0;
76+
self.reserve(size_hint);
77+
self.set_len(self.len() + size_hint());
78+
79+
for
80+
}
81+
}
82+

0 commit comments

Comments
 (0)