Skip to content

Commit 9c6a46b

Browse files
committed
fiddlin'
1 parent 65a0c17 commit 9c6a46b

File tree

2 files changed

+36
-22
lines changed

2 files changed

+36
-22
lines changed

README.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,23 @@
11
% The Unsafe Rust Programming Language
22

3+
# NOTE: This is a draft document, and may contain serious errors
4+
35
**This document is about advanced functionality and low-level development practices
46
in the Rust Programming Language. Most of the things discussed won't matter
57
to the average Rust programmer. However if you wish to correctly write unsafe
68
code in Rust, this text contains invaluable information.**
79

8-
This document seeks to complement [The Rust Programming Language Book][trpl] (TRPL).
10+
The Unsafe Rust Programming Language (TURPL) seeks to complement
11+
[The Rust Programming Language Book][trpl] (TRPL).
912
Where TRPL introduces the language and teaches the basics, TURPL dives deep into
1013
the specification of the language, and all the nasty bits necessary to write
1114
Unsafe Rust. TURPL does not assume you have read TRPL, but does assume you know
1215
the basics of the language and systems programming. We will not explain the
1316
stack or heap, we will not explain the syntax.
1417

1518

19+
20+
1621
# A Tale Of Two Languages
1722

1823
Rust can be thought of as two different languages: Safe Rust, and Unsafe Rust.

data.md

Lines changed: 30 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,23 +21,25 @@ An enum is said to be *C-like* if none of its variants have associated data.
2121
For all these, individual fields are aligned to their preferred alignment. For
2222
primitives this is usually equal to their size. For instance, a u32 will be
2323
aligned to a multiple of 32 bits, and a u16 will be aligned to a multiple of 16
24-
bits. Composite structures will have their size rounded up to be a multiple of
25-
the highest alignment required by their fields, and an alignment requirement
26-
equal to the highest alignment required by their fields. So for instance,
24+
bits. Composite structures will have a preferred alignment equal to the maximum
25+
of their fields' preferred alignment, and a size equal to a multiple of their
26+
preferred alignment. This ensures that arrays of T can be correctly iterated
27+
by offsetting by their size. So for instance,
2728

2829
```rust
2930
struct A {
3031
a: u8,
31-
c: u64,
32-
b: u32,
32+
c: u32,
33+
b: u16,
3334
}
3435
```
3536

36-
will have a size that is a multiple of 64-bits, and 64-bit alignment.
37+
will have a size that is a multiple of 32-bits, and 32-bit alignment.
3738

3839
There is *no indirection* for these types; all data is stored contiguously as you would
39-
expect in C. However with the exception of arrays, the layout of data is not by
40-
default specified in Rust. Given the two following struct definitions:
40+
expect in C. However with the exception of arrays (which are densely packed and
41+
in-order), the layout of data is not by default specified in Rust. Given the two
42+
following struct definitions:
4143

4244
```rust
4345
struct A {
@@ -91,9 +93,9 @@ struct Foo<u32, u16> {
9193
```
9294

9395
The latter case quite simply wastes space. An optimal use of space therefore requires
94-
different monomorphizations to *have different field orderings*.
96+
different monomorphizations to have *different field orderings*.
9597

96-
**Note: this is a hypothetical optimization that is not yet implemented in Rust 1.0.0**
98+
**Note: this is a hypothetical optimization that is not yet implemented in Rust 1.0**
9799

98100
Enums make this consideration even more complicated. Naively, an enum such as:
99101

@@ -120,31 +122,32 @@ such a representation is ineffiecient. The classic case of this is Rust's
120122
"null pointer optimization". Given a pointer that is known to not be null
121123
(e.g. `&u32`), an enum can *store* a discriminant bit *inside* the pointer
122124
by using null as a special value. The net result is that
123-
`sizeof(Option<&T>) == sizeof<&T>`
125+
`size_of::<Option<&T>>() == size_of::<&T>()`
124126

125-
There are many types in Rust that are, or contain, "not null" pointers such as `Box<T>`, `Vec<T>`,
126-
`String`, `&T`, and `&mut T`. Similarly, one can imagine nested enums pooling their tags into
127-
a single descriminant, as they are by definition known to have a limited range of valid values.
128-
In principle enums can use fairly elaborate algorithms to cache bits throughout nested types
129-
with special constrained representations. As such it is *especially* desirable that we leave
130-
enum layout unspecified today.
127+
There are many types in Rust that are, or contain, "not null" pointers such as
128+
`Box<T>`, `Vec<T>`, `String`, `&T`, and `&mut T`. Similarly, one can imagine
129+
nested enums pooling their tags into a single descriminant, as they are by
130+
definition known to have a limited range of valid values. In principle enums can
131+
use fairly elaborate algorithms to cache bits throughout nested types with
132+
special constrained representations. As such it is *especially* desirable that
133+
we leave enum layout unspecified today.
131134

132135

133136

134137

135138
# Dynamically Sized Types (DSTs)
136139

137140
Rust also supports types without a statically known size. On the surface,
138-
this is a bit nonsensical: Rust must know the size of something in order to
139-
work with it. DSTs are generally produced as views, or through type-erasure
141+
this is a bit nonsensical: Rust *must* know the size of something in order to
142+
work with it! DSTs are generally produced as views, or through type-erasure
140143
of types that *do* have a known size. Due to their lack of a statically known
141144
size, these types can only exist *behind* some kind of pointer. They consequently
142145
produce a *fat* pointer consisting of the pointer and the information that
143146
*completes* them.
144147

145148
For instance, the slice type, `[T]`, is some statically unknown number of elements
146149
stored contiguously. `&[T]` consequently consists of a `(&T, usize)` pair that specifies
147-
where the slice starts, and how many elements it contains. Similarly Trait Objects
150+
where the slice starts, and how many elements it contains. Similarly, Trait Objects
148151
support interface-oriented type erasure through a `(data_ptr, vtable_ptr)` pair.
149152

150153
Structs can actually store a single DST directly as their last field, but this
@@ -158,6 +161,8 @@ struct Foo {
158161
}
159162
```
160163

164+
**NOTE: As of Rust 1.0 struct DSTs are broken if the last field has
165+
a variable position based on its alignment.**
161166

162167

163168

@@ -235,6 +240,7 @@ Rust allows you to specify alternative data layout strategies from the default.
235240

236241

237242

243+
238244
## repr(C)
239245

240246
This is the most important `repr`. It has fairly simple intent: do what C does.
@@ -262,6 +268,7 @@ the FFI boundary.
262268

263269

264270

271+
265272
## repr(packed)
266273

267274
`repr(packed)` forces rust to strip any padding, and only align the type to a
@@ -278,11 +285,13 @@ this should not be used.
278285
This repr is a modifier on `repr(C)` and `repr(rust)`.
279286

280287

288+
289+
281290
## repr(u8), repr(u16), repr(u32), repr(u64)
282291

283292
These specify the size to make a C-like enum. If the discriminant overflows the
284293
integer it has to fit in, it will be an error. You can manually ask Rust to
285294
allow this by setting the overflowing element to explicitly be 0. However Rust
286295
will not allow you to create an enum where two variants.
287296

288-
These reprs have no affect on struct or non-C-like enum.
297+
These reprs have no affect on a struct or non-C-like enum.

0 commit comments

Comments
 (0)