Fix minor style issues

pull/271/head
Yuki Okushi 3 years ago
parent 55de6fa3c1
commit 1fe5457479

@ -1,6 +1,6 @@
# The Rustonomicon
#### The Dark Arts of Unsafe Rust
## The Dark Arts of Unsafe Rust
> THE KNOWLEDGE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF UNLEASHING INDESCRIBABLE HORRORS THAT

@ -14,10 +14,7 @@ don't happen unless you tell it otherwise. For more details, see the
With that said, here's our working definition: variables and pointers *alias*
if they refer to overlapping regions of memory.
# Why Aliasing Matters
## Why Aliasing Matters
So why should we care about aliasing?
@ -130,6 +127,3 @@ Of course, a full aliasing model for Rust must also take into consideration thin
function calls (which may mutate things we don't see), raw pointers (which have
no aliasing requirements on their own), and UnsafeCell (which lets the referent
of an `&` be mutated).

@ -3,15 +3,18 @@
Now that we've got some basic code set up, we'll need a way to clone the `Arc`.
Basically, we need to:
1. Increment the atomic reference count
2. Construct a new instance of the `Arc` from the inner pointer
First, we need to get access to the `ArcInner`:
```rust,ignore
let inner = unsafe { self.ptr.as_ref() };
```
We can update the atomic reference count as follows:
```rust,ignore
let old_rc = inner.rc.fetch_add(1, Ordering::???);
```
@ -26,11 +29,13 @@ is described more in [the section on the `Drop` implementation for
ordering, see [the section on atomics](atomics.md).
Thus, the code becomes this:
```rust,ignore
let old_rc = inner.rc.fetch_add(1, Ordering::Relaxed);
```
We'll need to add another import to use `Ordering`:
```rust,ignore
use std::sync::atomic::Ordering;
```
@ -54,7 +59,8 @@ probably incredibly degenerate) if the reference count reaches `isize::MAX`
probably not about 2 billion threads (or about **9 quintillion** on some 64-bit
machines) incrementing the reference count at once. This is what we'll do.
It's pretty simple to implement this behaviour:
It's pretty simple to implement this behavior:
```rust,ignore
if old_rc >= isize::MAX as usize {
std::process::abort();
@ -62,6 +68,7 @@ if old_rc >= isize::MAX as usize {
```
Then, we need to return a new instance of the `Arc`:
```rust,ignore
Self {
ptr: self.ptr,
@ -70,6 +77,7 @@ Self {
```
Now, let's wrap this all up inside the `Clone` implementation:
```rust,ignore
use std::sync::atomic::Ordering;

@ -6,6 +6,7 @@ low enough, otherwise the data will live forever on the heap.
To do this, we can implement `Drop`.
Basically, we need to:
1. Decrement the reference count
2. If there is only one reference remaining to the data, then:
3. Atomically fence the data to prevent reordering of the use and deletion of
@ -13,6 +14,7 @@ Basically, we need to:
4. Drop the inner data
First, we'll need to get access to the `ArcInner`:
```rust,ignore
let inner = unsafe { self.ptr.as_ref() };
```
@ -21,6 +23,7 @@ Now, we need to decrement the reference count. To streamline our code, we can
also return if the returned value from `fetch_sub` (the value of the reference
count before decrementing it) is not equal to `1` (which happens when we are not
the last reference to the data).
```rust,ignore
if inner.rc.fetch_sub(1, Ordering::Relaxed) != 1 {
return;
@ -59,11 +62,13 @@ implementation of `Arc`][3]:
[3]: https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/alloc/src/sync.rs#L1440-L1467
To do this, we do the following:
```rust,ignore
atomic::fence(Ordering::Acquire);
```
We'll need to import `std::sync::atomic` itself:
```rust,ignore
use std::sync::atomic;
```
@ -80,6 +85,7 @@ This is safe as we know we have the last pointer to the `ArcInner` and that its
pointer is valid.
Now, let's wrap this all up inside the `Drop` implementation:
```rust,ignore
impl<T> Drop for Arc<T> {
fn drop(&mut self) {

@ -1,6 +1,7 @@
# Final Code
Here's the final code, with some added comments and re-ordered imports:
```rust
use std::marker::PhantomData;
use std::ops::Deref;

@ -21,6 +21,7 @@ pointer to the T's allocation, we might as well put the reference count in that
same allocation.
Naively, it would look something like this:
```rust,ignore
use std::sync::atomic;
@ -45,6 +46,7 @@ all the details on variance and drop check.
To fix the first problem, we can use `NonNull<T>`. Note that `NonNull<T>` is a
wrapper around a raw pointer that declares that:
* We are variant over `T`
* Our pointer is never null
@ -53,6 +55,7 @@ To fix the second problem, we can include a `PhantomData` marker containing an
ownership of a value of `ArcInner<T>` (which itself contains some `T`).
With these changes we get our final structure:
```rust,ignore
use std::marker::PhantomData;
use std::ptr::NonNull;

@ -22,10 +22,7 @@ semantics we want, the optimizations compilers want, and the inconsistent chaos
our hardware wants. *We* would like to just write programs and have them do
exactly what we said but, you know, fast. Wouldn't that be great?
# Compiler Reordering
## Compiler Reordering
Compilers fundamentally want to be able to do all sorts of complicated
transformations to reduce data dependencies and eliminate dead code. In
@ -54,10 +51,7 @@ able to make these kinds of optimizations, because they can seriously improve
performance. On the other hand, we'd also like to be able to depend on our
program *doing the thing we said*.
# Hardware Reordering
## Hardware Reordering
On the other hand, even if the compiler totally understood what we wanted and
respected our wishes, our hardware might instead get us in trouble. Trouble
@ -110,11 +104,7 @@ programming:
incorrect. If possible, concurrent algorithms should be tested on
weakly-ordered hardware.
# Data Accesses
## Data Accesses
The C++ memory model attempts to bridge the gap by allowing us to talk about the
*causality* of our program. Generally, this is by establishing a *happens
@ -156,9 +146,7 @@ propagated to other threads. The set of orderings Rust exposes are:
TODO: negative reasoning vs positive reasoning? TODO: "can't forget to
synchronize"
# Sequentially Consistent
## Sequentially Consistent
Sequentially Consistent is the most powerful of all, implying the restrictions
of all other orderings. Intuitively, a sequentially consistent operation
@ -182,10 +170,7 @@ mechanically trivial to downgrade atomic operations to have a weaker
consistency later on. Just change `SeqCst` to `Relaxed` and you're done! Of
course, proving that this transformation is *correct* is a whole other matter.
# Acquire-Release
## Acquire-Release
Acquire and Release are largely intended to be paired. Their names hint at their
use case: they're perfectly suited for acquiring and releasing locks, and
@ -233,10 +218,7 @@ On strongly-ordered platforms most accesses have release or acquire semantics,
making release and acquire often totally free. This is not the case on
weakly-ordered platforms.
# Relaxed
## Relaxed
Relaxed accesses are the absolute weakest. They can be freely re-ordered and
provide no happens-before relationship. Still, relaxed operations are still
@ -251,9 +233,5 @@ There's rarely a benefit in making an operation relaxed on strongly-ordered
platforms, since they usually provide release-acquire semantics anyway. However
relaxed operations can be cheaper on weakly-ordered platforms.
[C11-busted]: http://plv.mpi-sws.org/c11comp/popl15.pdf
[C++-model]: https://en.cppreference.com/w/cpp/atomic/memory_order

@ -4,7 +4,7 @@ This section documents (or will document) features that are provided by the stan
that `#![no_std]` developers have to deal with (i.e. provide) to build `#![no_std]` binary crates. A
(likely incomplete) list of such features is shown below:
- #[lang = "eh_personality"]
- #[lang = "start"]
- #[lang = "termination"]
- #[panic_implementation]
- `#[lang = "eh_personality"]`
- `#[lang = "start"]`
- `#[lang = "termination"]`
- `#[panic_implementation]`

@ -31,4 +31,3 @@ fn reinterpret(foo: Foo) -> Bar {
But this is, at best, annoying. For common conversions, Rust provides
more ergonomic alternatives.

@ -203,7 +203,7 @@ of an inspector's destructor might access that borrowed data.
Therefore, the drop checker forces all borrowed data in a value to
strictly outlive that value.
# An Escape Hatch
## An Escape Hatch
The precise rules that govern drop checking may be less restrictive in
the future.
@ -322,13 +322,13 @@ attribute makes the type vulnerable to misuse that the borrower
checker will not catch, inviting havoc. It is better to avoid adding
the attribute.
# A related side note about drop order
## A related side note about drop order
While the drop order of fields inside a struct is defined, relying on it is
fragile and subtle. When the order matters, it is better to use the
[`ManuallyDrop`] wrapper.
# Is that all about drop checker?
## Is that all about drop checker?
It turns out that when writing unsafe code, we generally don't need to
worry at all about doing the right thing for the drop checker. However there

@ -30,10 +30,6 @@ it is not uncommon for Unsafe code to work with arrays of temporarily
uninitialized data while repeatedly invoking caller-provided code. Such code
needs to be careful and consider exception safety.
## Vec::push_all
`Vec::push_all` is a temporary hack to get extending a Vec by a slice reliably
@ -69,10 +65,6 @@ we *did* clone are dropped, we can set the `len` every loop iteration. If we
just want to guarantee that uninitialized memory can't be observed, we can set
the `len` after the loop.
## BinaryHeap::sift_up
Bubbling an element up a heap is a bit more complicated than extending a Vec.

@ -3,11 +3,7 @@
Most of the time, we expect types to have a statically known and positive size.
This isn't always the case in Rust.
# Dynamically Sized Types (DSTs)
## Dynamically Sized Types (DSTs)
Rust supports Dynamically Sized Types (DSTs): types without a statically
known size or alignment. On the surface, this is a bit nonsensical: Rust *must*
@ -69,11 +65,7 @@ fn main() {
(Yes, custom DSTs are a largely half-baked feature for now.)
# Zero Sized Types (ZSTs)
## Zero Sized Types (ZSTs)
Rust also allows types to be specified that occupy no space:
@ -121,9 +113,7 @@ type.
[alloc]: ../std/alloc/trait.GlobalAlloc.html#tymethod.alloc
[ub]: what-unsafe-does.html
# Empty Types
## Empty Types
Rust also enables types to be declared that *cannot even be instantiated*. These
types can only be talked about at the type level, and never at the value level.
@ -177,11 +167,7 @@ into a reference without any safety problems. It still doesn't prevent you from
trying to read or write values, but at least it compiles to a no-op instead
of UB.
# Extern Types
## Extern Types
There is [an accepted RFC][extern-types] to add proper types with an unknown size,
called *extern types*, which would let Rust developers model things like C's `void*`
@ -189,9 +175,6 @@ and other "declared but never defined" types more accurately. However as of
Rust 2018, the feature is stuck in limbo over how `size_of::<MyExternType>()`
should behave.
[dst-issue]: https://github.com/rust-lang/rust/issues/26403
[extern-types]: https://github.com/rust-lang/rfcs/blob/master/text/1861-extern-types.md
[`str`]: ../std/primitive.str.html

@ -1,6 +1,6 @@
# Foreign Function Interface
# Introduction
## Introduction
This guide will use the [snappy](https://github.com/google/snappy)
compression/decompression library as an introduction to writing bindings for
@ -85,7 +85,7 @@ extern {
# fn main() {}
```
# Creating a safe interface
## Creating a safe interface
The raw C API needs to be wrapped to provide memory safety and make use of higher-level concepts
like vectors. A library can choose to expose only the safe, high-level interface and hide the unsafe
@ -234,7 +234,7 @@ mod tests {
}
```
# Destructors
## Destructors
Foreign libraries often hand off ownership of resources to the calling code.
When this occurs, we must use Rust's destructors to provide safety and guarantee
@ -242,7 +242,7 @@ the release of these resources (especially in the case of panic).
For more about destructors, see the [Drop trait](../std/ops/trait.Drop.html).
# Callbacks from C code to Rust functions
## Callbacks from C code to Rust functions
Some external libraries require the usage of callbacks to report back their
current state or intermediate data to the caller.
@ -295,7 +295,6 @@ void trigger_callback() {
In this example Rust's `main()` will call `trigger_callback()` in C,
which would, in turn, call back to `callback()` in Rust.
## Targeting callbacks to Rust objects
The former example showed how a global function can be called from C code.
@ -384,7 +383,7 @@ This can be achieved by unregistering the callback in the object's
destructor and designing the library in a way that guarantees that no
callback will be performed after deregistration.
# Linking
## Linking
The `link` attribute on `extern` blocks provides the basic building block for
instructing rustc how it will link to native libraries. There are two accepted
@ -433,7 +432,7 @@ A few examples of how this model can be used are:
On macOS, frameworks behave with the same semantics as a dynamic library.
# Unsafe blocks
## Unsafe blocks
Some operations, like dereferencing raw pointers or calling functions that have been marked
unsafe are only allowed inside unsafe blocks. Unsafe blocks isolate unsafety and are a promise to
@ -448,7 +447,7 @@ unsafe fn kaboom(ptr: *const i32) -> i32 { *ptr }
This function can only be called from an `unsafe` block or another `unsafe` function.
# Accessing foreign globals
## Accessing foreign globals
Foreign APIs often export a global variable which could do something like track
global state. In order to access these variables, you declare them in `extern`
@ -498,7 +497,7 @@ fn main() {
Note that all interaction with a `static mut` is unsafe, both reading and
writing. Dealing with global mutable state requires a great deal of care.
# Foreign calling conventions
## Foreign calling conventions
Most foreign code exposes a C ABI, and Rust uses the platform's C calling convention by default when
calling foreign functions. Some foreign functions, most notably the Windows API, use other calling
@ -540,7 +539,7 @@ however, windows uses the `C` calling convention, so `C` would be used. This
means that in our previous example, we could have used `extern "system" { ... }`
to define a block for all windows systems, not only x86 ones.
# Interoperability with foreign code
## Interoperability with foreign code
Rust guarantees that the layout of a `struct` is compatible with the platform's
representation in C only if the `#[repr(C)]` attribute is applied to it.
@ -565,7 +564,7 @@ The [`libc` crate on crates.io][libc] includes type aliases and function
definitions for the C standard library in the `libc` module, and Rust links
against `libc` and `libm` by default.
# Variadic functions
## Variadic functions
In C, functions can be 'variadic', meaning they accept a variable number of arguments. This can
be achieved in Rust by specifying `...` within the argument list of a foreign function declaration:
@ -590,7 +589,7 @@ Normal Rust functions can *not* be variadic:
fn foo(x: i32, ...) { }
```
# The "nullable pointer optimization"
## The "nullable pointer optimization"
Certain Rust types are defined to never be `null`. This includes references (`&T`,
`&mut T`), boxes (`Box<T>`), and function pointers (`extern "abi" fn()`). When
@ -655,7 +654,7 @@ void register(void (*f)(int (*)(int), int)) {
No `transmute` required!
# Calling Rust code from C
## Calling Rust code from C
You may wish to compile Rust code in a way so that it can be called from C. This is
fairly easy, but requires a few things:
@ -673,7 +672,7 @@ discussed above in "[Foreign Calling
Conventions](ffi.html#foreign-calling-conventions)". The `no_mangle`
attribute turns off Rust's name mangling, so that it is easier to link to.
# FFI and panics
## FFI and panics
Its important to be mindful of `panic!`s when working with FFI. A `panic!`
across an FFI boundary is undefined behavior. If youre writing code that may
@ -702,7 +701,7 @@ for more information.
[`catch_unwind`]: ../std/panic/fn.catch_unwind.html
# Representing opaque structs
## Representing opaque structs
Sometimes, a C library wants to provide a pointer to something, but not let you
know the internal details of the thing it wants. The simplest way is to use a

@ -49,8 +49,6 @@ library:
* `Rc`
* `thread::scoped::JoinGuard`
## Drain
`drain` is a collections API that moves data out of the container without
@ -105,9 +103,6 @@ mem::forget us in the middle of the iteration, all that does is *leak even more*
Since we've accepted that mem::forget is safe, this is definitely safe. We call
leaks causing more leaks a *leak amplification*.
## Rc
Rc is an interesting case because at first glance it doesn't appear to be a
@ -177,9 +172,6 @@ This can be solved by just checking the `ref_count` and doing *something*. The
standard library's stance is to just abort, because your program has become
horribly degenerate. Also *oh my gosh* it's such a ridiculous corner case.
## thread::scoped::JoinGuard
The thread::scoped API intended to allow threads to be spawned that reference

@ -70,9 +70,7 @@ blows up in our face!
This program is clearly correct according to the reference semantics we actually
care about, but the lifetime system is too coarse-grained to handle that.
# Improperly reduced borrows
## Improperly reduced borrows
The following code fails to compile, because Rust doesn't understand that the borrow
is no longer needed and conservatively falls back to using a whole scope for it.
@ -120,5 +118,4 @@ error[E0499]: cannot borrow `*map` as mutable more than once at a time
| |_____- returning this value requires that `*map` is borrowed for `'m`
```
[ex2]: lifetimes.html#example-aliasing-a-mutable-reference

@ -87,9 +87,7 @@ z = y;
}
```
# Example: references that outlive referents
## Example: references that outlive referents
Alright, let's look at some of those examples from before:
@ -169,11 +167,7 @@ we could have returned an `&'a str` would have been if it was in a field of the
can be considered to reside at the bottom of the stack; though this limits
our implementation *just a bit*.)
# Example: aliasing a mutable reference
## Example: aliasing a mutable reference
How about the other example:
@ -222,9 +216,7 @@ to the compiler. However it does mean that several programs that are totally
correct with respect to Rust's *true* semantics are rejected because lifetimes
are too dumb.
# The area covered by a lifetime
## The area covered by a lifetime
The lifetime (sometimes called a *borrow*) is *alive* from the place it is
created to its last use. The borrowed thing needs to outlive only borrows that

@ -3,10 +3,7 @@
Rust allows you to specify alternative data layout strategies from the default.
There's also the [unsafe code guidelines] (note that it's **NOT** normative).
# repr(C)
## repr(C)
This is the most important `repr`. It has fairly simple intent: do what C does.
The order, size, and alignment of fields is exactly what you would expect from C
@ -57,9 +54,7 @@ construct an instance of an enum that does not match one of its
variants. (This allows exhaustive matches to continue to be written and
compiled as normal.)
# repr(transparent)
## repr(transparent)
This can only be used on structs with a single non-zero-sized field (there may
be additional zero-sized fields). The effect is that the layout and ABI of the
@ -75,9 +70,7 @@ Foo(f32)` to always have the same ABI as `f32`.
More details are in the [RFC][rfc-transparent].
# repr(u*), repr(i*)
## repr(u*), repr(i*)
These specify the size to make a fieldless enum. If the discriminant overflows
the integer it has to fit in, it will produce a compile-time error. You can
@ -101,10 +94,7 @@ optimization.
These reprs have no effect on a struct.
# repr(packed)
## repr(packed)
`repr(packed)` forces Rust to strip any padding, and only align the type to a
byte. This may improve the memory footprint, but will likely have other negative
@ -124,10 +114,7 @@ this should not be used.
This repr is a modifier on `repr(C)` and `repr(Rust)`.
# repr(align(n))
## repr(align(n))
`repr(align(n))` (where `n` is a power of two) forces the type to have an
alignment of *at least* n.
@ -139,10 +126,6 @@ kinds of concurrent code).
This is a modifier on `repr(C)` and `repr(Rust)`. It is incompatible with
`repr(packed)`.
[unsafe code guidelines]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html
[drop flags]: drop-flags.html
[ub loads]: https://github.com/rust-lang/rust/issues/27060

@ -63,4 +63,3 @@ naive scope analysis would be insufficient to prevent this bug, because `data`
does in fact live as long as we needed. However it was *changed* while we had
a reference into it. This is why Rust requires any references to freeze the
referent and its owners.

@ -1,4 +1,4 @@
## #[panic_handler]
# #[panic_handler]
`#[panic_handler]` is used to define the behavior of `panic!` in `#![no_std]` applications.
The `#[panic_handler]` attribute must be applied to a function with signature `fn(&PanicInfo)

@ -15,7 +15,6 @@ we will then relate it back to how subtyping actually occurs in Rust.
So here's our simple extension, *Objective Rust*, featuring three new types:
```rust
trait Animal {
fn snuggle(&self);
@ -133,10 +132,7 @@ because nothing ever has type `'a`. Lifetimes only occur as part of some larger
like `&'a u32` or `IterMut<'a, u32>`. To apply lifetime subtyping, we need to know
how to compose subtyping. Once again, we need *variance*.
# Variance
## Variance
Variance is where things get a bit complicated.
@ -442,4 +438,3 @@ struct MyType<'a, 'b, A: 'a, B: 'b, C, D, E, F, G, H, In, Out, Mixed> {
k2: Mixed, // invariant over Mixed, because invariance wins all conflicts
}
```

@ -50,7 +50,6 @@ Also of course you can get all of the functionality of these functions using raw
pointer casts or `union`s, but without any of the lints or other basic sanity
checks. Raw pointer casts and `union`s do not magically avoid the above rules.
[unbounded lifetime]: ./unbounded-lifetimes.md
[transmute]: ../std/mem/fn.transmute.html
[transmute_copy]: ../std/mem/fn.transmute_copy.html

@ -33,4 +33,3 @@ way to bound a lifetime is to return it from a function with a bound lifetime.
However if this is unacceptable, the reference can be placed in a location with
a specific lifetime. Unfortunately it's impossible to name all lifetimes involved
in a function.

@ -80,10 +80,12 @@ This code proceeds in three steps:
It's worth spending a bit more time on the loop in the middle, and in particular
the assignment operator and its interaction with `drop`. If we would have
written something like
written something like:
```rust,ignore
*x[i].as_mut_ptr() = Box::new(i as u32); // WRONG!
```
we would actually overwrite a `Box<u32>`, leading to `drop` of uninitialized
data, which will cause much sadness and pain.
@ -126,6 +128,7 @@ Note that, to use the `ptr` methods, you need to first obtain a *raw pointer* to
the data you want to initialize. It is illegal to construct a *reference* to
uninitialized data, which implies that you have to be careful when obtaining
said raw pointer:
* For an array of `T`, you can use `base_ptr.add(idx)` where `base_ptr: *mut T`
to compute the address of array index `idx`. This relies on
how arrays are laid out in memory.
@ -133,9 +136,9 @@ how arrays are laid out in memory.
also cannot use `&mut base_ptr.field` as that would be creating a
reference. Thus, it is currently not possible to create a raw pointer to a field
of a partially initialized struct, and also not possible to initialize a single
field of a partially initialized struct. (A
field of a partially initialized struct. (a
[solution to this problem](https://github.com/rust-lang/rfcs/pull/2582) is being
worked on.)
worked on).
One last remark: when reading old Rust code, you might stumble upon the
deprecated `mem::uninitialized` function. That function used to be the only way

@ -129,7 +129,6 @@ Because IntoIter takes ownership of its allocation, it needs to implement Drop
to free it. However it also wants to implement Drop to drop any elements it
contains that weren't yielded.
```rust,ignore
impl<T> Drop for IntoIter<T> {
fn drop(&mut self) {

@ -13,9 +13,6 @@ zero-sized types. We need to be careful of two things:
Thankfully we abstracted out pointer-iterators and allocating handling into
`RawValIter` and `RawVec` respectively. How mysteriously convenient.
## Allocating Zero-Sized Types
So if the allocator API doesn't support zero-sized allocations, what on earth
@ -103,9 +100,6 @@ impl<T> Drop for RawVec<T> {
That's it. We support pushing and popping zero-sized types now. Our iterators
(that aren't provided by slice Deref) are still busted, though.
## Iterating Zero-Sized Types
Zero-sized offsets are no-ops. This means that our current design will always

@ -117,4 +117,3 @@ it prevents us from having to trust all the safe code in the universe from messi
with our trusted state.
Safety lives!

Loading…
Cancel
Save