Merge pull request #271 from JohnTitor/clean-up

Update some wording making reference to issues/RFCs
pull/280/head
Eric Huss 4 years ago committed by GitHub
commit f30bc440db
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -1,6 +1,6 @@
# The Rustonomicon # 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, > 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 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* With that said, here's our working definition: variables and pointers *alias*
if they refer to overlapping regions of memory. if they refer to overlapping regions of memory.
## Why Aliasing Matters
# Why Aliasing Matters
So why should we care about aliasing? 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 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 no aliasing requirements on their own), and UnsafeCell (which lets the referent
of an `&` be mutated). 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`. Now that we've got some basic code set up, we'll need a way to clone the `Arc`.
Basically, we need to: Basically, we need to:
1. Increment the atomic reference count 1. Increment the atomic reference count
2. Construct a new instance of the `Arc` from the inner pointer 2. Construct a new instance of the `Arc` from the inner pointer
First, we need to get access to the `ArcInner`: First, we need to get access to the `ArcInner`:
```rust,ignore ```rust,ignore
let inner = unsafe { self.ptr.as_ref() }; let inner = unsafe { self.ptr.as_ref() };
``` ```
We can update the atomic reference count as follows: We can update the atomic reference count as follows:
```rust,ignore ```rust,ignore
let old_rc = inner.rc.fetch_add(1, Ordering::???); 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). ordering, see [the section on atomics](atomics.md).
Thus, the code becomes this: Thus, the code becomes this:
```rust,ignore ```rust,ignore
let old_rc = inner.rc.fetch_add(1, Ordering::Relaxed); let old_rc = inner.rc.fetch_add(1, Ordering::Relaxed);
``` ```
We'll need to add another import to use `Ordering`: We'll need to add another import to use `Ordering`:
```rust,ignore ```rust,ignore
use std::sync::atomic::Ordering; 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 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. 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 ```rust,ignore
if old_rc >= isize::MAX as usize { if old_rc >= isize::MAX as usize {
std::process::abort(); 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`: Then, we need to return a new instance of the `Arc`:
```rust,ignore ```rust,ignore
Self { Self {
ptr: self.ptr, ptr: self.ptr,
@ -70,6 +77,7 @@ Self {
``` ```
Now, let's wrap this all up inside the `Clone` implementation: Now, let's wrap this all up inside the `Clone` implementation:
```rust,ignore ```rust,ignore
use std::sync::atomic::Ordering; 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`. To do this, we can implement `Drop`.
Basically, we need to: Basically, we need to:
1. Decrement the reference count 1. Decrement the reference count
2. If there is only one reference remaining to the data, then: 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 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 4. Drop the inner data
First, we'll need to get access to the `ArcInner`: First, we'll need to get access to the `ArcInner`:
```rust,ignore ```rust,ignore
let inner = unsafe { self.ptr.as_ref() }; 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 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 count before decrementing it) is not equal to `1` (which happens when we are not
the last reference to the data). the last reference to the data).
```rust,ignore ```rust,ignore
if inner.rc.fetch_sub(1, Ordering::Relaxed) != 1 { if inner.rc.fetch_sub(1, Ordering::Relaxed) != 1 {
return; 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 [3]: https://github.com/rust-lang/rust/blob/e1884a8e3c3e813aada8254edfa120e85bf5ffca/library/alloc/src/sync.rs#L1440-L1467
To do this, we do the following: To do this, we do the following:
```rust,ignore ```rust,ignore
atomic::fence(Ordering::Acquire); atomic::fence(Ordering::Acquire);
``` ```
We'll need to import `std::sync::atomic` itself: We'll need to import `std::sync::atomic` itself:
```rust,ignore ```rust,ignore
use std::sync::atomic; 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. pointer is valid.
Now, let's wrap this all up inside the `Drop` implementation: Now, let's wrap this all up inside the `Drop` implementation:
```rust,ignore ```rust,ignore
impl<T> Drop for Arc<T> { impl<T> Drop for Arc<T> {
fn drop(&mut self) { fn drop(&mut self) {

@ -1,6 +1,7 @@
# Final Code # Final Code
Here's the final code, with some added comments and re-ordered imports: Here's the final code, with some added comments and re-ordered imports:
```rust ```rust
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ops::Deref; 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. same allocation.
Naively, it would look something like this: Naively, it would look something like this:
```rust,ignore ```rust,ignore
use std::sync::atomic; 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 To fix the first problem, we can use `NonNull<T>`. Note that `NonNull<T>` is a
wrapper around a raw pointer that declares that: wrapper around a raw pointer that declares that:
* We are variant over `T` * We are variant over `T`
* Our pointer is never null * 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`). ownership of a value of `ArcInner<T>` (which itself contains some `T`).
With these changes we get our final structure: With these changes we get our final structure:
```rust,ignore ```rust,ignore
use std::marker::PhantomData; use std::marker::PhantomData;
use std::ptr::NonNull; 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 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? 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 Compilers fundamentally want to be able to do all sorts of complicated
transformations to reduce data dependencies and eliminate dead code. In 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 performance. On the other hand, we'd also like to be able to depend on our
program *doing the thing we said*. program *doing the thing we said*.
## Hardware Reordering
# Hardware Reordering
On the other hand, even if the compiler totally understood what we wanted and 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 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 incorrect. If possible, concurrent algorithms should be tested on
weakly-ordered hardware. weakly-ordered hardware.
## Data Accesses
# Data Accesses
The C++ memory model attempts to bridge the gap by allowing us to talk about the 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 *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 TODO: negative reasoning vs positive reasoning? TODO: "can't forget to
synchronize" synchronize"
## Sequentially Consistent
# Sequentially Consistent
Sequentially Consistent is the most powerful of all, implying the restrictions Sequentially Consistent is the most powerful of all, implying the restrictions
of all other orderings. Intuitively, a sequentially consistent operation 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 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. 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 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 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 making release and acquire often totally free. This is not the case on
weakly-ordered platforms. weakly-ordered platforms.
## Relaxed
# Relaxed
Relaxed accesses are the absolute weakest. They can be freely re-ordered and Relaxed accesses are the absolute weakest. They can be freely re-ordered and
provide no happens-before relationship. Still, relaxed operations are still 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 platforms, since they usually provide release-acquire semantics anyway. However
relaxed operations can be cheaper on weakly-ordered platforms. relaxed operations can be cheaper on weakly-ordered platforms.
[C11-busted]: http://plv.mpi-sws.org/c11comp/popl15.pdf [C11-busted]: http://plv.mpi-sws.org/c11comp/popl15.pdf
[C++-model]: https://en.cppreference.com/w/cpp/atomic/memory_order [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 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: (likely incomplete) list of such features is shown below:
- #[lang = "eh_personality"] - `#[lang = "eh_personality"]`
- #[lang = "start"] - `#[lang = "start"]`
- #[lang = "termination"] - `#[lang = "termination"]`
- #[panic_implementation] - `#[panic_implementation]`

@ -23,18 +23,18 @@ Here's an exhaustive list of all the true casts. For brevity, we will use `*`
to denote either a `*const` or `*mut`, and `integer` to denote any integral to denote either a `*const` or `*mut`, and `integer` to denote any integral
primitive: primitive:
* `*T as *U` where `T, U: Sized` * `*T as *U` where `T, U: Sized`
* `*T as *U` TODO: explain unsized situation * `*T as *U` TODO: explain unsized situation
* `*T as integer` * `*T as integer`
* `integer as *T` * `integer as *T`
* `number as number` * `number as number`
* `field-less enum as integer` * `field-less enum as integer`
* `bool as integer` * `bool as integer`
* `char as integer` * `char as integer`
* `u8 as char` * `u8 as char`
* `&[T; n] as *const T` * `&[T; n] as *const T`
* `fn as *T` where `T: Sized` * `fn as *T` where `T: Sized`
* `fn as integer` * `fn as integer`
Note that lengths are not adjusted when casting raw slices - Note that lengths are not adjusted when casting raw slices -
`*const [u16] as *const [u8]` creates a slice that only includes `*const [u16] as *const [u8]` creates a slice that only includes

@ -31,4 +31,3 @@ fn reinterpret(foo: Foo) -> Bar {
But this is, at best, annoying. For common conversions, Rust provides But this is, at best, annoying. For common conversions, Rust provides
more ergonomic alternatives. 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 Therefore, the drop checker forces all borrowed data in a value to
strictly outlive that value. strictly outlive that value.
# An Escape Hatch ## An Escape Hatch
The precise rules that govern drop checking may be less restrictive in The precise rules that govern drop checking may be less restrictive in
the future. the future.
@ -322,13 +322,13 @@ attribute makes the type vulnerable to misuse that the borrow
checker will not catch, inviting havoc. It is better to avoid adding checker will not catch, inviting havoc. It is better to avoid adding
the attribute. 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 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 fragile and subtle. When the order matters, it is better to use the
[`ManuallyDrop`] wrapper. [`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 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 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 uninitialized data while repeatedly invoking caller-provided code. Such code
needs to be careful and consider exception safety. needs to be careful and consider exception safety.
## Vec::push_all ## Vec::push_all
`Vec::push_all` is a temporary hack to get extending a Vec by a slice reliably `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 just want to guarantee that uninitialized memory can't be observed, we can set
the `len` after the loop. the `len` after the loop.
## BinaryHeap::sift_up ## BinaryHeap::sift_up
Bubbling an element up a heap is a bit more complicated than extending a Vec. 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. Most of the time, we expect types to have a statically known and positive size.
This isn't always the case in Rust. 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 Rust supports Dynamically Sized Types (DSTs): types without a statically
known size or alignment. On the surface, this is a bit nonsensical: Rust *must* 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.) (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: Rust also allows types to be specified that occupy no space:
@ -121,9 +113,7 @@ type.
[alloc]: ../std/alloc/trait.GlobalAlloc.html#tymethod.alloc [alloc]: ../std/alloc/trait.GlobalAlloc.html#tymethod.alloc
[ub]: what-unsafe-does.html [ub]: what-unsafe-does.html
## Empty Types
# Empty Types
Rust also enables types to be declared that *cannot even be instantiated*. These 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. types can only be talked about at the type level, and never at the value level.
@ -177,22 +167,15 @@ 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 trying to read or write values, but at least it compiles to a no-op instead
of UB. of UB.
## Extern Types
# Extern Types
There is [an accepted RFC][extern-types] to add proper types with an unknown size, 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*` called *extern types*, which would let Rust developers model things like C's `void*`
and other "declared but never defined" types more accurately. However as of 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>()` Rust 2018, [the feature is stuck in limbo over how `size_of_val::<MyExternType>()`
should behave. should behave][extern-types-issue].
[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 [extern-types]: https://github.com/rust-lang/rfcs/blob/master/text/1861-extern-types.md
[extern-types-issue]: https://github.com/rust-lang/rust/issues/43467
[`str`]: ../std/primitive.str.html [`str`]: ../std/primitive.str.html
[slice]: ../std/primitive.slice.html [slice]: ../std/primitive.slice.html

@ -1,6 +1,6 @@
# Foreign Function Interface # Foreign Function Interface
# Introduction ## Introduction
This guide will use the [snappy](https://github.com/google/snappy) This guide will use the [snappy](https://github.com/google/snappy)
compression/decompression library as an introduction to writing bindings for compression/decompression library as an introduction to writing bindings for
@ -85,7 +85,7 @@ extern {
# fn main() {} # 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 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 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. 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 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). 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 Some external libraries require the usage of callbacks to report back their
current state or intermediate data to the caller. 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, In this example Rust's `main()` will call `trigger_callback()` in C,
which would, in turn, call back to `callback()` in Rust. which would, in turn, call back to `callback()` in Rust.
## Targeting callbacks to Rust objects ## Targeting callbacks to Rust objects
The former example showed how a global function can be called from C code. 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 destructor and designing the library in a way that guarantees that no
callback will be performed after deregistration. callback will be performed after deregistration.
# Linking ## Linking
The `link` attribute on `extern` blocks provides the basic building block for 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 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. 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 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 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. 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 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` 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 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. 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 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 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" { ... }` means that in our previous example, we could have used `extern "system" { ... }`
to define a block for all windows systems, not only x86 ones. 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 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. 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 definitions for the C standard library in the `libc` module, and Rust links
against `libc` and `libm` by default. 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 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: 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, ...) { } 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`, 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 `&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! 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 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: 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` Conventions](ffi.html#foreign-calling-conventions)". The `no_mangle`
attribute turns off Rust's name mangling, so that it is easier to link to. 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!` 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 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 [`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 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 know the internal details of the thing it wants. The simplest way is to use a

@ -49,8 +49,6 @@ library:
* `Rc` * `Rc`
* `thread::scoped::JoinGuard` * `thread::scoped::JoinGuard`
## Drain ## Drain
`drain` is a collections API that moves data out of the container without `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 Since we've accepted that mem::forget is safe, this is definitely safe. We call
leaks causing more leaks a *leak amplification*. leaks causing more leaks a *leak amplification*.
## Rc ## Rc
Rc is an interesting case because at first glance it doesn't appear to be a 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 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. horribly degenerate. Also *oh my gosh* it's such a ridiculous corner case.
## thread::scoped::JoinGuard ## thread::scoped::JoinGuard
The thread::scoped API intended to allow threads to be spawned that reference 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 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. 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 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. 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` | |_____- returning this value requires that `*map` is borrowed for `'m`
``` ```
[ex2]: lifetimes.html#example-aliasing-a-mutable-reference [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: 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 can be considered to reside at the bottom of the stack; though this limits
our implementation *just a bit*.) our implementation *just a bit*.)
## Example: aliasing a mutable reference
# Example: aliasing a mutable reference
How about the other example: 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 correct with respect to Rust's *true* semantics are rejected because lifetimes
are too dumb. 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 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 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. 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). 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. 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 The order, size, and alignment of fields is exactly what you would expect from C
@ -18,7 +15,7 @@ reinterpreting values as a different type.
We strongly recommend using [rust-bindgen][] and/or [cbindgen][] to manage your FFI We strongly recommend using [rust-bindgen][] and/or [cbindgen][] to manage your FFI
boundaries for you. The Rust team works closely with those projects to ensure boundaries for you. The Rust team works closely with those projects to ensure
that they work robustly and are compatible with current and future guarantees that they work robustly and are compatible with current and future guarantees
about type layouts and reprs. about type layouts and `repr`s.
The interaction of `repr(C)` with Rust's more exotic data layout features must be The interaction of `repr(C)` with Rust's more exotic data layout features must be
kept in mind. Due to its dual purpose as "for FFI" and "for layout control", kept in mind. Due to its dual purpose as "for FFI" and "for layout control",
@ -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 variants. (This allows exhaustive matches to continue to be written and
compiled as normal.) compiled as normal.)
## repr(transparent)
# repr(transparent)
This can only be used on structs with a single non-zero-sized field (there may 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 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]. 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 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 the integer it has to fit in, it will produce a compile-time error. You can
@ -99,12 +92,9 @@ manipulate its tag and fields. See [the RFC][really-tagged] for details.
Adding an explicit `repr` to an enum suppresses the null-pointer Adding an explicit `repr` to an enum suppresses the null-pointer
optimization. optimization.
These reprs have no effect on a struct. These `repr`s 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 `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 byte. This may improve the memory footprint, but will likely have other negative
@ -117,17 +107,15 @@ compiler might be able to paper over alignment issues with shifts and masks.
However if you take a reference to a packed field, it's unlikely that the However if you take a reference to a packed field, it's unlikely that the
compiler will be able to emit code to avoid an unaligned load. compiler will be able to emit code to avoid an unaligned load.
**[As of Rust 2018, this still can cause undefined behavior.][ub loads]** [As this can cause undefined behavior][ub loads], the lint has been implemented
and it will become a hard error.
`repr(packed)` is not to be used lightly. Unless you have extreme requirements, `repr(packed)` is not to be used lightly. Unless you have extreme requirements,
this should not be used. this should not be used.
This repr is a modifier on `repr(C)` and `repr(Rust)`. 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 `repr(align(n))` (where `n` is a power of two) forces the type to have an
alignment of *at least* n. alignment of *at least* n.
@ -139,10 +127,6 @@ kinds of concurrent code).
This is a modifier on `repr(C)` and `repr(Rust)`. It is incompatible with This is a modifier on `repr(C)` and `repr(Rust)`. It is incompatible with
`repr(packed)`. `repr(packed)`.
[unsafe code guidelines]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html [unsafe code guidelines]: https://rust-lang.github.io/unsafe-code-guidelines/layout.html
[drop flags]: drop-flags.html [drop flags]: drop-flags.html
[ub loads]: https://github.com/rust-lang/rust/issues/27060 [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 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 a reference into it. This is why Rust requires any references to freeze the
referent and its owners. 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. `#[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) 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: So here's our simple extension, *Objective Rust*, featuring three new types:
```rust ```rust
trait Animal { trait Animal {
fn snuggle(&self); 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 like `&'a u32` or `IterMut<'a, u32>`. To apply lifetime subtyping, we need to know
how to compose subtyping. Once again, we need *variance*. how to compose subtyping. Once again, we need *variance*.
## Variance
# Variance
Variance is where things get a bit complicated. Variance is where things get a bit complicated.
@ -447,4 +443,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 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 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. checks. Raw pointer casts and `union`s do not magically avoid the above rules.
[unbounded lifetime]: ./unbounded-lifetimes.md [unbounded lifetime]: ./unbounded-lifetimes.md
[transmute]: ../std/mem/fn.transmute.html [transmute]: ../std/mem/fn.transmute.html
[transmute_copy]: ../std/mem/fn.transmute_copy.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 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 a specific lifetime. Unfortunately it's impossible to name all lifetimes involved
in a function. 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 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 the assignment operator and its interaction with `drop`. If we would have
written something like written something like:
```rust,ignore ```rust,ignore
*x[i].as_mut_ptr() = Box::new(i as u32); // WRONG! *x[i].as_mut_ptr() = Box::new(i as u32); // WRONG!
``` ```
we would actually overwrite a `Box<u32>`, leading to `drop` of uninitialized we would actually overwrite a `Box<u32>`, leading to `drop` of uninitialized
data, which will cause much sadness and pain. 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 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 uninitialized data, which implies that you have to be careful when obtaining
said raw pointer: said raw pointer:
* For an array of `T`, you can use `base_ptr.add(idx)` where `base_ptr: *mut T` * 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 to compute the address of array index `idx`. This relies on
how arrays are laid out in memory. 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 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 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 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 [solution to this problem](https://github.com/rust-lang/rust/issues/64490) is being
worked on.) worked on).
One last remark: when reading old Rust code, you might stumble upon the 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 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 to free it. However it also wants to implement Drop to drop any elements it
contains that weren't yielded. contains that weren't yielded.
```rust,ignore ```rust,ignore
impl<T> Drop for IntoIter<T> { impl<T> Drop for IntoIter<T> {
fn drop(&mut self) { 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 Thankfully we abstracted out pointer-iterators and allocating handling into
`RawValIter` and `RawVec` respectively. How mysteriously convenient. `RawValIter` and `RawVec` respectively. How mysteriously convenient.
## Allocating Zero-Sized Types ## Allocating Zero-Sized Types
So if the allocator API doesn't support zero-sized allocations, what on earth 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's it. We support pushing and popping zero-sized types now. Our iterators
(that aren't provided by slice Deref) are still busted, though. (that aren't provided by slice Deref) are still busted, though.
## Iterating Zero-Sized Types ## Iterating Zero-Sized Types
Zero-sized offsets are no-ops. This means that our current design will always 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. with our trusted state.
Safety lives! Safety lives!

Loading…
Cancel
Save