|
|
|
@ -6,25 +6,29 @@ interact?
|
|
|
|
|
The separation between Safe Rust and Unsafe Rust is controlled with the
|
|
|
|
|
`unsafe` keyword, which acts as an interface from one to the other. This is
|
|
|
|
|
why we can say Safe Rust is a safe language: all the unsafe parts are kept
|
|
|
|
|
exclusively behind the boundary.
|
|
|
|
|
exclusively behind the `unsafe` boundary. If you wish, you can even toss
|
|
|
|
|
`#![forbid(unsafe_code)]` into your code base to statically guarantee that
|
|
|
|
|
you're only writing Safe Rust.
|
|
|
|
|
|
|
|
|
|
The `unsafe` keyword has two uses: to declare the existence of contracts the
|
|
|
|
|
compiler can't check, and to declare that the adherence of some code to
|
|
|
|
|
those contracts has been checked by the programmer.
|
|
|
|
|
compiler can't check, and to declare that a programmer has checked that these
|
|
|
|
|
contracts have been upheld.
|
|
|
|
|
|
|
|
|
|
You can use `unsafe` to indicate the existence of unchecked contracts on
|
|
|
|
|
_functions_ and on _trait declarations_. On functions, `unsafe` means that
|
|
|
|
|
_functions_ and _trait declarations_. On functions, `unsafe` means that
|
|
|
|
|
users of the function must check that function's documentation to ensure
|
|
|
|
|
they are using it in a way that maintains the contracts the function
|
|
|
|
|
requires. On trait declarations, `unsafe` means that implementors of the
|
|
|
|
|
trait must check the trait documentation to ensure their implementation
|
|
|
|
|
maintains the contracts the trait requires.
|
|
|
|
|
|
|
|
|
|
You can use `unsafe` on a block to declare that all constraints required
|
|
|
|
|
by an unsafe function within the block have been adhered to, and the code
|
|
|
|
|
can therefore be trusted. You can use `unsafe` on a trait implementation
|
|
|
|
|
to declare that the implementation of that trait has adhered to whatever
|
|
|
|
|
contracts the trait's documentation requires.
|
|
|
|
|
You can use `unsafe` on a block to declare that all unsafe actions performed
|
|
|
|
|
within are verified to uphold the contracts of those operations. For instance,
|
|
|
|
|
the index passed to `slice::get_unchecked` is in-bounds.
|
|
|
|
|
|
|
|
|
|
You can use `unsafe` on a trait implementation to declare that the implementation
|
|
|
|
|
upholds the trait's contract. For instance, that a type implementing `Send` is
|
|
|
|
|
really safe to move to another thread.
|
|
|
|
|
|
|
|
|
|
The standard library has a number of unsafe functions, including:
|
|
|
|
|
|
|
|
|
@ -32,11 +36,10 @@ The standard library has a number of unsafe functions, including:
|
|
|
|
|
memory safety to be freely violated.
|
|
|
|
|
* `mem::transmute` reinterprets some value as having a given type, bypassing
|
|
|
|
|
type safety in arbitrary ways (see [conversions] for details).
|
|
|
|
|
* Every raw pointer to a sized type has an intrinstic `offset` method that
|
|
|
|
|
invokes Undefined Behavior if the passed offset is not "in bounds" as
|
|
|
|
|
defined by LLVM.
|
|
|
|
|
* All FFI functions are `unsafe` because the other language can do arbitrary
|
|
|
|
|
operations that the Rust compiler can't check.
|
|
|
|
|
* Every raw pointer to a sized type has an `offset` method that
|
|
|
|
|
invokes Undefined Behavior if the passed offset is not ["in bounds"][ptr_offset].
|
|
|
|
|
* All FFI functions are `unsafe` to call because the other language can do
|
|
|
|
|
arbitrary operations that the Rust compiler can't check.
|
|
|
|
|
|
|
|
|
|
As of Rust 1.0 there are exactly two unsafe traits:
|
|
|
|
|
|
|
|
|
@ -45,41 +48,60 @@ As of Rust 1.0 there are exactly two unsafe traits:
|
|
|
|
|
* `Sync` is a marker trait that promises threads can safely share implementors
|
|
|
|
|
through a shared reference.
|
|
|
|
|
|
|
|
|
|
Much of the Rust standard library also uses Unsafe Rust internally, although
|
|
|
|
|
these implementations are rigorously manually checked, and the Safe Rust
|
|
|
|
|
interfaces provided on top of these implementations can be assumed to be safe.
|
|
|
|
|
Much of the Rust standard library also uses Unsafe Rust internally. These
|
|
|
|
|
implementations have generally been rigorously manually checked, so the Safe Rust
|
|
|
|
|
interfaces built on top of these implementations can be assumed to be safe.
|
|
|
|
|
|
|
|
|
|
The need for all of this separation boils down a single fundamental property
|
|
|
|
|
of Safe Rust:
|
|
|
|
|
|
|
|
|
|
**No matter what, Safe Rust can't cause Undefined Behavior.**
|
|
|
|
|
|
|
|
|
|
The design of the safe/unsafe split means that Safe Rust inherently has to
|
|
|
|
|
trust that any Unsafe Rust it touches has been written correctly (meaning
|
|
|
|
|
the Unsafe Rust actually maintains whatever contracts it is supposed to
|
|
|
|
|
maintain). On the other hand, Unsafe Rust has to be very careful about
|
|
|
|
|
trusting Safe Rust.
|
|
|
|
|
The design of the safe/unsafe split means that there is an asymmetric trust
|
|
|
|
|
relationship between Safe and Unsafe Rust. Safe Rust inherently has to
|
|
|
|
|
trust that any Unsafe Rust it touches has been written correctly.
|
|
|
|
|
On the other hand, Unsafe Rust has to be very careful about trusting Safe Rust.
|
|
|
|
|
|
|
|
|
|
As an example, Rust has the `PartialOrd` and `Ord` traits to differentiate
|
|
|
|
|
between types which can "just" be compared, and those that provide a total
|
|
|
|
|
ordering (where every value of the type is either equal to, greater than,
|
|
|
|
|
or less than any other value of the same type). The sorted map type
|
|
|
|
|
`BTreeMap` doesn't make sense for partially-ordered types, and so it
|
|
|
|
|
requires that any key type for it implements the `Ord` trait. However,
|
|
|
|
|
`BTreeMap` has Unsafe Rust code inside of its implementation, and this
|
|
|
|
|
Unsafe Rust code cannot assume that any `Ord` implementation it gets makes
|
|
|
|
|
sense. The unsafe portions of `BTreeMap`'s internals have to be careful to
|
|
|
|
|
maintain all necessary contracts, even if a key type's `Ord` implementation
|
|
|
|
|
does not implement a total ordering.
|
|
|
|
|
|
|
|
|
|
Unsafe Rust cannot automatically trust Safe Rust. When writing Unsafe Rust,
|
|
|
|
|
you must be careful to only rely on specific Safe Rust code, and not make
|
|
|
|
|
assumptions about potential future Safe Rust code providing the same
|
|
|
|
|
guarantees.
|
|
|
|
|
|
|
|
|
|
This is the problem that `unsafe` traits exist to resolve. The `BTreeMap`
|
|
|
|
|
type could theoretically require that keys implement a new trait called
|
|
|
|
|
`UnsafeOrd`, rather than `Ord`, that might look like this:
|
|
|
|
|
between types which can "just" be compared, and those that provide a "total"
|
|
|
|
|
ordering (which basically means that comparison behaves reasonably).
|
|
|
|
|
|
|
|
|
|
`BTreeMap` doesn't really make sense for partially-ordered types, and so it
|
|
|
|
|
requires that its keys implement `Ord`. However, `BTreeMap` has Unsafe Rust code
|
|
|
|
|
inside of its implementation. Because it would be unacceptable for a sloppy `Ord`
|
|
|
|
|
implementation (which is Safe to write) to cause Undefined Behavior, the Unsafe
|
|
|
|
|
code in BTreeMap must be written to be robust against `Ord` implementations which
|
|
|
|
|
aren't actually total — even though that's the whole point of requiring `Ord`.
|
|
|
|
|
|
|
|
|
|
The Unsafe Rust code just can't trust the Safe Rust code to be written correctly.
|
|
|
|
|
That said, `BTreeMap` will still behave completely erratically if you feed in
|
|
|
|
|
values that don't have a total ordering. It just won't ever cause Undefined
|
|
|
|
|
Behavior.
|
|
|
|
|
|
|
|
|
|
One may wonder, if `BTreeMap` cannot trust `Ord` because it's Safe, why can it
|
|
|
|
|
trust *any* Safe code? For instance `BTreeMap` relies on integers and slices to
|
|
|
|
|
be implemented correctly. Those are safe too, right?
|
|
|
|
|
|
|
|
|
|
The difference is one of scope. When `BTreeMap` relies on integers and slices,
|
|
|
|
|
it's relying on one very specific implementation. This is a measured risk that
|
|
|
|
|
can be weighed against the benefit. In this case there's basically zero risk;
|
|
|
|
|
if integers and slices are broken, *everyone* is broken. Also, they're maintained
|
|
|
|
|
by the same people who maintain `BTreeMap`, so it's easy to keep tabs on them.
|
|
|
|
|
|
|
|
|
|
On the other hand, `BTreeMap`'s key type is generic. Trusting its `Ord` implementation
|
|
|
|
|
means trusting every `Ord` implementation in the past, present, and future.
|
|
|
|
|
Here the risk is high: someone somewhere is going to make a mistake and mess up
|
|
|
|
|
their `Ord` implementation, or even just straight up lie about providing a total
|
|
|
|
|
ordering because "it seems to work". When that happens, `BTreeMap` needs to be
|
|
|
|
|
prepared.
|
|
|
|
|
|
|
|
|
|
The same logic applies to trusting a closure that's passed to you to behave
|
|
|
|
|
correctly.
|
|
|
|
|
|
|
|
|
|
This problem of unbounded generic trust is the problem that `unsafe` traits
|
|
|
|
|
exist to resolve. The `BTreeMap` type could theoretically require that keys
|
|
|
|
|
implement a new trait called `UnsafeOrd`, rather than `Ord`, that might look
|
|
|
|
|
like this:
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
use std::cmp::Ordering;
|
|
|
|
@ -92,32 +114,32 @@ unsafe trait UnsafeOrd {
|
|
|
|
|
Then, a type would use `unsafe` to implement `UnsafeOrd`, indicating that
|
|
|
|
|
they've ensured their implementation maintains whatever contracts the
|
|
|
|
|
trait expects. In this situation, the Unsafe Rust in the internals of
|
|
|
|
|
`BTreeMap` could trust that the key type's `UnsafeOrd` implementation is
|
|
|
|
|
correct. If it isn't, it's the fault of the unsafe trait implementation
|
|
|
|
|
code, which is consistent with Rust's safety guarantees.
|
|
|
|
|
`BTreeMap` would be justified in trusting that the key type's `UnsafeOrd`
|
|
|
|
|
implementation is correct. If it isn't, it's the fault of the unsafe trait
|
|
|
|
|
implementation, which is consistent with Rust's safety guarantees.
|
|
|
|
|
|
|
|
|
|
The decision of whether to mark a trait `unsafe` is an API design choice.
|
|
|
|
|
Rust has traditionally avoided marking traits unsafe because it makes Unsafe
|
|
|
|
|
Rust pervasive, which is not desirable. `Send` and `Sync` are marked unsafe
|
|
|
|
|
Rust has traditionally avoided doing this because it makes Unsafe
|
|
|
|
|
Rust pervasive, which isn't desirable. `Send` and `Sync` are marked unsafe
|
|
|
|
|
because thread safety is a *fundamental property* that unsafe code can't
|
|
|
|
|
possibly hope to defend against in the way it could defend against a bad
|
|
|
|
|
`Ord` implementation. The decision of whether to mark your own traits `unsafe`
|
|
|
|
|
depends on the same sort of consideration. If `unsafe` code cannot reasonably
|
|
|
|
|
depends on the same sort of consideration. If `unsafe` code can't reasonably
|
|
|
|
|
expect to defend against a bad implementation of the trait, then marking the
|
|
|
|
|
trait `unsafe` is a reasonable choice.
|
|
|
|
|
|
|
|
|
|
As an aside, while `Send` and `Sync` are `unsafe` traits, they are
|
|
|
|
|
As an aside, while `Send` and `Sync` are `unsafe` traits, they are *also*
|
|
|
|
|
automatically implemented for types when such derivations are provably safe
|
|
|
|
|
to do. `Send` is automatically derived for all types composed only of values
|
|
|
|
|
whose types also implement `Send`. `Sync` is automatically derived for all
|
|
|
|
|
types composed only of values whose types also implement `Sync`.
|
|
|
|
|
types composed only of values whose types also implement `Sync`. This minimizes
|
|
|
|
|
the pervasive unsafety of making these two traits `unsafe`.
|
|
|
|
|
|
|
|
|
|
This is the dance of Safe Rust and Unsafe Rust. It is designed to make using
|
|
|
|
|
Safe Rust as ergonomic as possible, but requires extra effort and care when
|
|
|
|
|
writing Unsafe Rust. The rest of the book is largely a discussion of the sort
|
|
|
|
|
of care that must be taken, and what contracts it is expected of Unsafe Rust
|
|
|
|
|
to uphold.
|
|
|
|
|
This is the balance between Safe and Unsafe Rust. The separation is designed to
|
|
|
|
|
make using Safe Rust as ergonomic as possible, but requires extra effort and
|
|
|
|
|
care when writing Unsafe Rust. The rest of this book is largely a discussion
|
|
|
|
|
of the sort of care that must be taken, and what contracts Unsafe Rust must uphold.
|
|
|
|
|
|
|
|
|
|
[drop flags]: drop-flags.html
|
|
|
|
|
[conversions]: conversions.html
|
|
|
|
|
[ptr_offset]: ../std/primitive.pointer.html#method.offset
|
|
|
|
|
|
|
|
|
|