Skip to content
This repository was archived by the owner on Sep 5, 2023. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions nomicon/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
% The Rustonomicon
# The Rustonomicon

#### The Dark Arts of Advanced and Unsafe Rust Programming

**NOTE: This is a draft document, and may contain serious errors**
# NOTE: This is a draft document, and may contain serious errors

> Instead of the programs I had hoped for, there came only a shuddering blackness
and ineffable loneliness; and I saw at last a fearful truth which no one had
Expand Down Expand Up @@ -35,4 +35,4 @@ exception-safety, pointer aliasing, memory models, and even some type-theory.
We will also be spending a lot of time talking about the different kinds
of safety and guarantees.

[trpl]: ../book/
[trpl]: ../book/index.html
3 changes: 3 additions & 0 deletions nomicon/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Summary

[Introduction](README.md)

* [Meet Safe and Unsafe](meet-safe-and-unsafe.md)
* [How Safe and Unsafe Interact](safe-unsafe-meaning.md)
* [Working with Unsafe](working-with-unsafe.md)
Expand Down Expand Up @@ -51,3 +53,4 @@
* [Handling Zero-Sized Types](vec-zsts.md)
* [Final Code](vec-final.md)
* [Implementing Arc and Mutex](arc-and-mutex.md)
* [FFI](ffi.md)
2 changes: 1 addition & 1 deletion nomicon/arc-and-mutex.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Implementing Arc and Mutex
# Implementing Arc and Mutex

Knowing the theory is all fine and good, but the *best* way to understand
something is to use it. To better understand atomics and interior mutability,
Expand Down
12 changes: 6 additions & 6 deletions nomicon/atomics.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Atomics
# Atomics

Rust pretty blatantly just inherits C11's memory model for atomics. This is not
due to this model being particularly excellent or easy to understand. Indeed,
Expand All @@ -24,10 +24,10 @@ exactly what we said but, you know, fast. Wouldn't that be great?

# Compiler Reordering

Compilers fundamentally want to be able to do all sorts of crazy transformations
to reduce data dependencies and eliminate dead code. In particular, they may
radically change the actual order of events, or make events never occur! If we
write something like
Compilers fundamentally want to be able to do all sorts of complicated
transformations to reduce data dependencies and eliminate dead code. In
particular, they may radically change the actual order of events, or make events
never occur! If we write something like

```rust,ignore
x = 1;
Expand Down Expand Up @@ -196,7 +196,7 @@ reordered to occur before it.

When thread A releases a location in memory and then thread B subsequently
acquires *the same* location in memory, causality is established. Every write
that happened before A's release will be observed by B after its release.
that happened before A's release will be observed by B after its acquisition.
However no causality is established with any other threads. Similarly, no
causality is established if A and B access *different* locations in memory.

Expand Down
2 changes: 1 addition & 1 deletion nomicon/borrow-splitting.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Splitting Borrows
# Splitting Borrows

The mutual exclusion property of mutable references can be very limiting when
working with a composite structure. The borrow checker understands some basic
Expand Down
2 changes: 1 addition & 1 deletion nomicon/casts.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Casts
# Casts

Casts are a superset of coercions: every coercion can be explicitly
invoked via a cast. However some conversions require a cast.
Expand Down
1 change: 1 addition & 0 deletions nomicon/chapter_1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Chapter 1
2 changes: 1 addition & 1 deletion nomicon/checked-uninit.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Checked Uninitialized Memory
# Checked Uninitialized Memory

Like C, all stack variables in Rust are uninitialized until a value is
explicitly assigned to them. Unlike C, Rust statically prevents you from ever
Expand Down
6 changes: 4 additions & 2 deletions nomicon/coercions.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Coercions
# Coercions

Types can implicitly be coerced to change in certain contexts. These changes are
generally just *weakening* of types, largely focused around pointers and
Expand All @@ -17,6 +17,7 @@ Coercion is allowed between the following types:
* `&T` to `*const T`
* `&mut T` to `*mut T`
* Unsizing: `T` to `U` if `T` implements `CoerceUnsized<U>`
* Deref coercion: Expression `&x` of type `&T` to `&*x` of type `&U` if `T` derefs to `U` (i.e. `T: Deref<Target=U>`)

`CoerceUnsized<Pointer<U>> for Pointer<T> where T: Unsize<U>` is implemented
for all pointer types (including smart pointers like Box and Rc). Unsize is
Expand All @@ -27,8 +28,9 @@ only implemented automatically, and enables the following transformations:
* `Foo<..., T, ...>` => `Foo<..., U, ...>` where:
* `T: Unsize<U>`
* `Foo` is a struct
* Only the last field of `Foo` has type `T`
* Only the last field of `Foo` has type involving `T`
* `T` is not part of the type of any other fields
* `Bar<T>: Unsize<Bar<U>>`, if the last field of `Foo` has type `Bar<T>`

Coercions occur at a *coercion site*. Any location that is explicitly typed
will cause a coercion to its type. If inference is necessary, the coercion will
Expand Down
2 changes: 1 addition & 1 deletion nomicon/concurrency.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Concurrency and Parallelism
# Concurrency and Parallelism

Rust as a language doesn't *really* have an opinion on how to do concurrency or
parallelism. The standard library exposes OS threads and blocking sys-calls
Expand Down
2 changes: 1 addition & 1 deletion nomicon/constructors.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Constructors
# Constructors

There is exactly one way to create an instance of a user-defined type: name it,
and initialize all its fields at once:
Expand Down
2 changes: 1 addition & 1 deletion nomicon/conversions.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Type Conversions
# Type Conversions

At the end of the day, everything is just a pile of bits somewhere, and type
systems are just there to help us use those bits right. There are two common
Expand Down
2 changes: 1 addition & 1 deletion nomicon/data.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Data Representation in Rust
# Data Representation in Rust

Low-level programming cares a lot about data layout. It's a big deal. It also
pervasively influences the rest of the language, so we're going to start by
Expand Down
24 changes: 12 additions & 12 deletions nomicon/destructors.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Destructors
# Destructors

What the language *does* provide is full-blown automatic destructors through the
`Drop` trait, which provides the following method:
Expand Down Expand Up @@ -26,7 +26,7 @@ this is totally fine.
For instance, a custom implementation of `Box` might write `Drop` like this:

```rust
#![feature(alloc, heap_api, drop_in_place, unique)]
#![feature(alloc, heap_api, unique)]

extern crate alloc;

Expand All @@ -40,8 +40,8 @@ struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> {
fn drop(&mut self) {
unsafe {
drop_in_place(*self.ptr);
heap::deallocate((*self.ptr) as *mut u8,
drop_in_place(self.ptr.as_ptr());
heap::deallocate(self.ptr.as_ptr() as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
}
Expand All @@ -57,7 +57,7 @@ use-after-free the `ptr` because when drop exits, it becomes inaccessible.
However this wouldn't work:

```rust
#![feature(alloc, heap_api, drop_in_place, unique)]
#![feature(alloc, heap_api, unique)]

extern crate alloc;

Expand All @@ -71,8 +71,8 @@ struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> {
fn drop(&mut self) {
unsafe {
drop_in_place(*self.ptr);
heap::deallocate((*self.ptr) as *mut u8,
drop_in_place(self.ptr.as_ptr());
heap::deallocate(self.ptr.as_ptr() as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
}
Expand All @@ -86,7 +86,7 @@ impl<T> Drop for SuperBox<T> {
unsafe {
// Hyper-optimized: deallocate the box's contents for it
// without `drop`ing the contents
heap::deallocate((*self.my_box.ptr) as *mut u8,
heap::deallocate(self.my_box.ptr.as_ptr() as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
}
Expand Down Expand Up @@ -135,7 +135,7 @@ The classic safe solution to overriding recursive drop and allowing moving out
of Self during `drop` is to use an Option:

```rust
#![feature(alloc, heap_api, drop_in_place, unique)]
#![feature(alloc, heap_api, unique)]

extern crate alloc;

Expand All @@ -149,8 +149,8 @@ struct Box<T>{ ptr: Unique<T> }
impl<T> Drop for Box<T> {
fn drop(&mut self) {
unsafe {
drop_in_place(*self.ptr);
heap::deallocate((*self.ptr) as *mut u8,
drop_in_place(self.ptr.as_ptr());
heap::deallocate(self.ptr.as_ptr() as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
}
Expand All @@ -166,7 +166,7 @@ impl<T> Drop for SuperBox<T> {
// without `drop`ing the contents. Need to set the `box`
// field as `None` to prevent Rust from trying to Drop it.
let my_box = self.my_box.take().unwrap();
heap::deallocate((*my_box.ptr) as *mut u8,
heap::deallocate(my_box.ptr.as_ptr() as *mut u8,
mem::size_of::<T>(),
mem::align_of::<T>());
mem::forget(my_box);
Expand Down
2 changes: 1 addition & 1 deletion nomicon/dot-operator.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% The Dot Operator
# The Dot Operator

The dot operator will perform a lot of magic to convert types. It will perform
auto-referencing, auto-dereferencing, and coercion until types match.
Expand Down
18 changes: 3 additions & 15 deletions nomicon/drop-flags.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Drop Flags
# Drop Flags

The examples in the previous section introduce an interesting problem for Rust.
We have seen that it's possible to conditionally initialize, deinitialize, and
Expand Down Expand Up @@ -79,17 +79,5 @@ if condition {
}
```

As of Rust 1.0, the drop flags are actually not-so-secretly stashed in a hidden
field of any type that implements Drop. Rust sets the drop flag by overwriting
the entire value with a particular bit pattern. This is pretty obviously Not
The Fastest and causes a bunch of trouble with optimizing code. It's legacy from
a time when you could do much more complex conditional initialization.

As such work is currently under way to move the flags out onto the stack frame
where they more reasonably belong. Unfortunately, this work will take some time
as it requires fairly substantial changes to the compiler.

Regardless, Rust programs don't need to worry about uninitialized values on
the stack for correctness. Although they might care for performance. Thankfully,
Rust makes it easy to take control here! Uninitialized values are there, and
you can work with them in Safe Rust, but you're never in danger.
The drop flags are tracked on the stack and no longer stashed in types that
implement drop.
34 changes: 26 additions & 8 deletions nomicon/dropck.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Drop Check
# Drop Check

We have seen how lifetimes provide us some fairly simple rules for ensuring
that we never read dangling references. However up to this point we have only ever
Expand Down Expand Up @@ -125,7 +125,7 @@ is that some Drop implementations will not access borrowed data even
though their type gives them the capability for such access.

For example, this variant of the above `Inspector` example will never
accessed borrowed data:
access borrowed data:

```rust,ignore
struct Inspector<'a>(&'a u8, &'static str);
Expand Down Expand Up @@ -199,24 +199,42 @@ assert (unsafely) that a generic type's destructor is *guaranteed* to
not access any expired data, even if its type gives it the capability
to do so.

That attribute is called `unsafe_destructor_blind_to_params`.
That attribute is called `may_dangle` and was introduced in [RFC 1327]
(https://github.com/rust-lang/rfcs/blob/master/text/1327-dropck-param-eyepatch.md).
To deploy it on the Inspector example from above, we would write:

```rust,ignore
struct Inspector<'a>(&'a u8, &'static str);

impl<'a> Drop for Inspector<'a> {
#[unsafe_destructor_blind_to_params]
unsafe impl<#[may_dangle] 'a> Drop for Inspector<'a> {
fn drop(&mut self) {
println!("Inspector(_, {}) knows when *not* to inspect.", self.1);
}
}
```

This attribute has the word `unsafe` in it because the compiler is not
checking the implicit assertion that no potentially expired data
Use of this attribute requires the `Drop` impl to be marked `unsafe` because the
compiler is not checking the implicit assertion that no potentially expired data
(e.g. `self.0` above) is accessed.

The attribute can be applied to any number of lifetime and type parameters. In
the following example, we assert that we access no data behind a reference of
lifetime `'b` and that the only uses of `T` will be moves or drops, but omit
the attribute from `'a` and `U`, because we do access data with that lifetime
and that type:

```rust,ignore
use std::fmt::Display;

struct Inspector<'a, 'b, T, U: Display>(&'a u8, &'b u8, T, U);

unsafe impl<'a, #[may_dangle] 'b, #[may_dangle] T, U: Display> Drop for Inspector<'a, 'b, T, U> {
fn drop(&mut self) {
println!("Inspector({}, _, _, {})", self.0, self.3);
}
}
```

It is sometimes obvious that no such access can occur, like the case above.
However, when dealing with a generic type parameter, such access can
occur indirectly. Examples of such indirect access are:
Expand Down Expand Up @@ -263,7 +281,7 @@ some other method invoked by the destructor, rather than being written
directly within it.

In all of the above cases where the `&'a u8` is accessed in the
destructor, adding the `#[unsafe_destructor_blind_to_params]`
destructor, adding the `#[may_dangle]`
attribute makes the type vulnerable to misuse that the borrower
checker will not catch, inviting havoc. It is better to avoid adding
the attribute.
Expand Down
6 changes: 3 additions & 3 deletions nomicon/exception-safety.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Exception Safety
# Exception Safety

Although programs should use unwinding sparingly, there's a lot of code that
*can* panic. If you unwrap a None, index out of bounds, or divide by 0, your
Expand Down Expand Up @@ -93,7 +93,7 @@ uselessly. We would rather have the following:
```text
bubble_up(heap, index):
let elem = heap[index]
while index != 0 && element < heap[parent(index)]:
while index != 0 && elem < heap[parent(index)]:
heap[index] = heap[parent(index)]
index = parent(index)
heap[index] = elem
Expand Down Expand Up @@ -137,7 +137,7 @@ If Rust had `try` and `finally` like in Java, we could do the following:
bubble_up(heap, index):
let elem = heap[index]
try:
while index != 0 && element < heap[parent(index)]:
       while index != 0 && elem < heap[parent(index)]:
heap[index] = heap[parent(index)]
index = parent(index)
finally:
Expand Down
2 changes: 1 addition & 1 deletion nomicon/exotic-sizes.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
% Exotically Sized Types
# Exotically Sized Types

Most of the time, we think in terms of types with a fixed, positive size. This
is not always the case, however.
Expand Down
Loading