Skip to content

Commit 43697dd

Browse files
committed
---
yaml --- r: 228987 b: refs/heads/try c: e4f718a h: refs/heads/master i: 228985: 3caf844 228983: 16e0a69 v: v3
1 parent e6e0e91 commit 43697dd

File tree

2 files changed

+28
-29
lines changed

2 files changed

+28
-29
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
refs/heads/master: aca2057ed5fb7af3f8905b2bc01f72fa001c35c8
33
refs/heads/snap-stage3: 1af31d4974e33027a68126fa5a5a3c2c6491824f
4-
refs/heads/try: ccb08a52fee9756f39696253e1cd1d1b3b55fc85
4+
refs/heads/try: e4f718ad1c91e4da0438cf925a2baad39fa70181
55
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
66
refs/tags/release-0.2: c870d2dffb391e14efb05aa27898f1f6333a9596
77
refs/tags/release-0.3: b5f0d0f648d9a6153664837026ba1be43d3e2503

branches/try/uninitialized.md

Lines changed: 27 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ of bits that may or may not even reflect a valid state for the type that is
66
supposed to inhabit that location of memory. Attempting to interpret this memory
77
as a value of *any* type will cause Undefined Behaviour. Do Not Do This.
88

9-
Like C, all stack variables in Rust begin their life as uninitialized until a
9+
Like C, all stack variables in Rust are uninitialized until a
1010
value is explicitly assigned to them. Unlike C, Rust statically prevents you
1111
from ever reading them until you do:
1212

@@ -32,17 +32,14 @@ or anything like that. So this compiles:
3232
```rust
3333
fn main() {
3434
let x: i32;
35-
let y: i32;
36-
37-
y = 1;
3835

3936
if true {
4037
x = 1;
4138
} else {
4239
x = 2;
4340
}
4441

45-
println!("{} {}", x, y);
42+
println!("{}", x);
4643
}
4744
```
4845

@@ -98,13 +95,13 @@ to call the destructor of a variable that is conditionally initialized? It turns
9895
out that Rust actually tracks whether a type should be dropped or not *at
9996
runtime*. As a variable becomes initialized and uninitialized, a *drop flag* for
10097
that variable is set and unset. When a variable goes out of scope or is assigned
101-
it evaluates whether the current value of the variable should be dropped. Of
102-
course, static analysis can remove these checks. If the compiler can prove that
98+
a value, it evaluates whether the current value of the variable should be dropped.
99+
Of course, static analysis can remove these checks. If the compiler can prove that
103100
a value is guaranteed to be either initialized or not, then it can theoretically
104101
generate more efficient code! As such it may be desirable to structure code to
105102
have *static drop semantics* when possible.
106103

107-
As of Rust 1.0, the drop flags are actually not-so-secretly stashed in a secret
104+
As of Rust 1.0, the drop flags are actually not-so-secretly stashed in a hidden
108105
field of any type that implements Drop. The language sets the drop flag by
109106
overwriting the entire struct with a particular value. This is pretty obviously
110107
Not The Fastest and causes a bunch of trouble with optimizing code. As such work
@@ -115,7 +112,7 @@ requires fairly substantial changes to the compiler.
115112
So in general, Rust programs don't need to worry about uninitialized values on
116113
the stack for correctness. Although they might care for performance. Thankfully,
117114
Rust makes it easy to take control here! Uninitialized values are there, and
118-
Safe Rust lets you work with them, but you're never in trouble.
115+
Safe Rust lets you work with them, but you're never in danger.
119116

120117
One interesting exception to this rule is working with arrays. Safe Rust doesn't
121118
permit you to partially initialize an array. When you initialize an array, you
@@ -125,23 +122,23 @@ Unfortunately this is pretty rigid, especially if you need to initialize your
125122
array in a more incremental or dynamic way.
126123

127124
Unsafe Rust gives us a powerful tool to handle this problem:
128-
`std::mem::uninitialized`. This function pretends to return a value when really
125+
`mem::uninitialized`. This function pretends to return a value when really
129126
it does nothing at all. Using it, we can convince Rust that we have initialized
130127
a variable, allowing us to do trickier things with conditional and incremental
131128
initialization.
132129

133-
Unfortunately, this raises a tricky problem. Assignment has a different meaning
134-
to Rust based on whether it believes that a variable is initialized or not. If
135-
it's uninitialized, then Rust will semantically just memcopy the bits over the
136-
uninit ones, and do nothing else. However if Rust believes a value to be
137-
initialized, it will try to `Drop` the old value! Since we've tricked Rust into
138-
believing that the value is initialized, we can no longer safely use normal
139-
assignment.
130+
Unfortunately, this opens us up to all kinds of problems. Assignment has a
131+
different meaning to Rust based on whether it believes that a variable is
132+
initialized or not. If it's uninitialized, then Rust will semantically just
133+
memcopy the bits over the uninitialized ones, and do nothing else. However if Rust
134+
believes a value to be initialized, it will try to `Drop` the old value!
135+
Since we've tricked Rust into believing that the value is initialized, we
136+
can no longer safely use normal assignment.
140137

141-
This is also a problem if you're working with a raw system allocator, which of
142-
course returns a pointer to uninitialized memory.
138+
This is also a problem if you're working with a raw system allocator, which
139+
returns a pointer to uninitialized memory.
143140

144-
To handle this, we must use the `std::ptr` module. In particular, it provides
141+
To handle this, we must use the `ptr` module. In particular, it provides
145142
three functions that allow us to assign bytes to a location in memory without
146143
evaluating the old value: `write`, `copy`, and `copy_nonoverlapping`.
147144

@@ -157,7 +154,7 @@ evaluating the old value: `write`, `copy`, and `copy_nonoverlapping`.
157154
It should go without saying that these functions, if misused, will cause serious
158155
havoc or just straight up Undefined Behaviour. The only things that these
159156
functions *themselves* require is that the locations you want to read and write
160-
are allocated. However the ways writing arbitrary bit patterns to arbitrary
157+
are allocated. However the ways writing arbitrary bits to arbitrary
161158
locations of memory can break things are basically uncountable!
162159

163160
Putting this all together, we get the following:
@@ -177,6 +174,7 @@ fn main() {
177174
x = mem::uninitialized();
178175
for i in 0..SIZE {
179176
// very carefully overwrite each index without reading it
177+
// NOTE: exception safety is not a concern; Box can't panic
180178
ptr::write(&mut x[i], Box::new(i));
181179
}
182180
}
@@ -186,15 +184,16 @@ fn main() {
186184
```
187185

188186
It's worth noting that you don't need to worry about ptr::write-style
189-
shenanigans with Plain Old Data (POD; types which don't implement Drop, nor
190-
contain Drop types), because Rust knows not to try to Drop them. Similarly you
191-
should be able to assign the POD fields of partially initialized structs
192-
directly.
187+
shenanigans with types which don't implement Drop or
188+
contain Drop types, because Rust knows not to try to Drop them. Similarly you
189+
should be able to assign to fields of partially initialized structs
190+
directly if those fields don't contain any Drop types.
193191

194-
However when working with uninitialized memory you need to be ever vigilant for
192+
However when working with uninitialized memory you need to be ever-vigilant for
195193
Rust trying to Drop values you make like this before they're fully initialized.
196-
So every control path through that variable's scope must initialize the value
197-
before it ends. *This includes code panicking*. Again, POD types need not worry.
194+
Every control path through that variable's scope must initialize the value
195+
before it ends, if has a destructor.
196+
*[This includes code panicking](unwinding.html)*.
198197

199198
And that's about it for working with uninitialized memory! Basically nothing
200199
anywhere expects to be handed uninitialized memory, so if you're going to pass

0 commit comments

Comments
 (0)