mirror of https://github.com/rust-lang/nomicon
The previous version of the chapter covered a lot of ground, but was a little meandering and hard to follow at times. This draft is intended to be clearer and more direct, while still providing the same information as the previous version.pull/10/head
parent
43185c0b2e
commit
bab35d7cdc
@ -1,150 +1,131 @@
|
|||||||
% How Safe and Unsafe Interact
|
% How Safe and Unsafe Interact
|
||||||
|
|
||||||
So what's the relationship between Safe and Unsafe Rust? How do they interact?
|
What's the relationship between Safe Rust and Unsafe Rust? How do they
|
||||||
|
interact?
|
||||||
Rust models the separation between Safe and Unsafe Rust with the `unsafe`
|
|
||||||
keyword, which can be thought as a sort of *foreign function interface* (FFI)
|
The separation between Safe Rust and Unsafe Rust is controlled with the
|
||||||
between Safe and Unsafe Rust. This is the magic behind why we can say Safe Rust
|
`unsafe` keyword, which acts as a sort of *foreign function interface*
|
||||||
is a safe language: all the scary unsafe bits are relegated exclusively to FFI
|
from one to the other. This boundary is why we can say Safe Rust is a
|
||||||
*just like every other safe language*.
|
safe language: all the unsafe parts are kept exclusively behind the FFI
|
||||||
|
boundary, *just like any other safe language*. Best of all, because Safe
|
||||||
However because one language is a subset of the other, the two can be cleanly
|
Rust is a subset of Unsafe Rust, the two can be cleanly intermixed,
|
||||||
intermixed as long as the boundary between Safe and Unsafe Rust is denoted with
|
without headers, runtimes, or any other FFI boilerplate.
|
||||||
the `unsafe` keyword. No need to write headers, initialize runtimes, or any of
|
|
||||||
that other FFI boiler-plate.
|
The `unsafe` keyword has dual purposes: to declare the existence of
|
||||||
|
contracts the compiler can't check, and to declare that the adherence
|
||||||
There are several places `unsafe` can appear in Rust today, which can largely be
|
of some code to those contracts has been checked by the programmer,
|
||||||
grouped into two categories:
|
and the code can therefore be trusted.
|
||||||
|
|
||||||
* There are unchecked contracts here. To declare you understand this, I require
|
You can use `unsafe` to indicate the existence of unchecked contracts on
|
||||||
you to write `unsafe` elsewhere:
|
_functions_ and on _trait declarations_. On functions, `unsafe` means that
|
||||||
* On functions, `unsafe` is declaring the function to be unsafe to call.
|
users of the function must check that function's documentation to ensure
|
||||||
Users of the function must check the documentation to determine what this
|
they are using it in a way that maintains the contracts the function
|
||||||
means, and then have to write `unsafe` somewhere to identify that they're
|
requires. On trait declarations, `unsafe` means that implementors of the
|
||||||
aware of the danger.
|
trait must check the trait documentation to ensure their implementation
|
||||||
* On trait declarations, `unsafe` is declaring that *implementing* the trait
|
maintains the contracts the trait requires.
|
||||||
is an unsafe operation, as it has contracts that other unsafe code is free
|
|
||||||
to trust blindly. (More on this below.)
|
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
|
||||||
* I am declaring that I have, to the best of my knowledge, adhered to the
|
can therefore be trusted. You can use `unsafe` on a trait implementation
|
||||||
unchecked contracts:
|
to declare that the implementation of that trait has adhered to whatever
|
||||||
* On trait implementations, `unsafe` is declaring that the contract of the
|
contracts the trait's documentation requires.
|
||||||
`unsafe` trait has been upheld.
|
|
||||||
* On blocks, `unsafe` is declaring any unsafety from an unsafe
|
There is also the `#[unsafe_no_drop_flag]` attribute, which exists for
|
||||||
operation within to be handled, and therefore the parent function is safe.
|
historic reasons and is being phased out. See the section on [drop flags]
|
||||||
|
for details.
|
||||||
There is also `#[unsafe_no_drop_flag]`, which is a special case that exists for
|
|
||||||
historical reasons and is in the process of being phased out. See the section on
|
The standard library has a number of unsafe functions, including:
|
||||||
[drop flags] for details.
|
|
||||||
|
* `slice::get_unchecked`, which performs unchecked indexing, allowing
|
||||||
Some examples of unsafe functions:
|
memory safety to be freely violated.
|
||||||
|
* `mem::transmute` reinterprets some value as having a given type, bypassing
|
||||||
* `slice::get_unchecked` will perform unchecked indexing, allowing memory
|
type safety in arbitrary ways (see [conversions] for details).
|
||||||
safety to be freely violated.
|
* Every raw pointer to a sized type has an intrinstic `offset` method that
|
||||||
* every raw pointer to sized type has intrinsic `offset` method that invokes
|
invokes Undefined Behavior if the passed offset is not "in bounds" as
|
||||||
Undefined Behavior if it is not "in bounds" as defined by LLVM.
|
defined by LLVM.
|
||||||
* `mem::transmute` reinterprets some value as having the given type,
|
* All FFI functions are `unsafe` because the other language can do arbitrary
|
||||||
bypassing type safety in arbitrary ways. (see [conversions] for details)
|
operations that the Rust compiler can't check.
|
||||||
* All FFI functions are `unsafe` because they can do arbitrary things.
|
|
||||||
C being an obvious culprit, but generally any language can do something
|
|
||||||
that Rust isn't happy about.
|
|
||||||
|
|
||||||
As of Rust 1.0 there are exactly two unsafe traits:
|
As of Rust 1.0 there are exactly two unsafe traits:
|
||||||
|
|
||||||
* `Send` is a marker trait (it has no actual API) that promises implementors
|
* `Send` is a marker trait (a trait with no API) that promises implementors are
|
||||||
are safe to send (move) to another thread.
|
safe to send (move) to another thread.
|
||||||
* `Sync` is a marker trait that promises that threads can safely share
|
* `Sync` is a marker trait that promises threads can safely share implementors
|
||||||
implementors through a shared reference.
|
through a shared reference.
|
||||||
|
|
||||||
The need for unsafe traits boils down to the fundamental property of safe code:
|
Much of the Rust standard library also uses Unsafe Rust internally, although
|
||||||
|
these implementations are rigorously manually checked, and the Safe Rust
|
||||||
**No matter how completely awful Safe code is, it can't cause Undefined
|
interfaces provided on top of these implementations can be assumed to be safe.
|
||||||
Behavior.**
|
|
||||||
|
The need for all of this separation boils down a single fundamental property
|
||||||
This means that Unsafe Rust, **the royal vanguard of Undefined Behavior**, has to be
|
of Safe Rust:
|
||||||
*super paranoid* about generic safe code. To be clear, Unsafe Rust is totally free to trust
|
|
||||||
specific safe code. Anything else would degenerate into infinite spirals of
|
**No matter what, Safe Rust can't cause Undefined Behavior.**
|
||||||
paranoid despair. In particular it's generally regarded as ok to trust the standard library
|
|
||||||
to be correct. `std` is effectively an extension of the language, and you
|
The design of the safe/unsafe split means that Safe Rust inherently has to
|
||||||
really just have to trust the language. If `std` fails to uphold the
|
trust that any Unsafe Rust it touches has been written correctly (meaning
|
||||||
guarantees it declares, then it's basically a language bug.
|
the Unsafe Rust actually maintains whatever contracts it is supposed to
|
||||||
|
maintain). On the other hand, Unsafe Rust has to be very careful about
|
||||||
That said, it would be best to minimize *needlessly* relying on properties of
|
trusting Safe Rust.
|
||||||
concrete safe code. Bugs happen! Of course, I must reinforce that this is only
|
|
||||||
a concern for Unsafe code. Safe code can blindly trust anyone and everyone
|
As an example, Rust has the `PartialOrd` and `Ord` traits to differentiate
|
||||||
as far as basic memory-safety is concerned.
|
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,
|
||||||
On the other hand, safe traits are free to declare arbitrary contracts, but because
|
or less than any other value of the same type). The sorted map type
|
||||||
implementing them is safe, unsafe code can't trust those contracts to actually
|
`BTreeMap` doesn't make sense for partially-ordered types, and so it
|
||||||
be upheld. This is different from the concrete case because *anyone* can
|
requires that any key type for it implements the `Ord` trait. However,
|
||||||
randomly implement the interface. There is something fundamentally different
|
`BTreeMap` has Unsafe Rust code inside of its implementation, and this
|
||||||
about trusting a particular piece of code to be correct, and trusting *all the
|
Unsafe Rust code cannot assume that any `Ord` implementation it gets makes
|
||||||
code that will ever be written* to be correct.
|
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
|
||||||
For instance Rust has `PartialOrd` and `Ord` traits to try to differentiate
|
does not implement a total ordering.
|
||||||
between types which can "just" be compared, and those that actually implement a
|
|
||||||
total ordering. Pretty much every API that wants to work with data that can be
|
Unsafe Rust cannot automatically trust Safe Rust. When writing Unsafe Rust,
|
||||||
compared wants Ord data. For instance, a sorted map like BTreeMap
|
you must be careful to only rely on specific Safe Rust code, and not make
|
||||||
*doesn't even make sense* for partially ordered types. If you claim to implement
|
assumptions about potential future Safe Rust code providing the same
|
||||||
Ord for a type, but don't actually provide a proper total ordering, BTreeMap will
|
guarantees.
|
||||||
get *really confused* and start making a total mess of itself. Data that is
|
|
||||||
inserted may be impossible to find!
|
This is the problem that `unsafe` traits exist to resolve. The `BTreeMap`
|
||||||
|
type could theoretically require that keys implement a new trait called
|
||||||
But that's okay. BTreeMap is safe, so it guarantees that even if you give it a
|
`UnsafeOrd`, rather than `Ord`, that might look like this:
|
||||||
completely garbage Ord implementation, it will still do something *safe*. You
|
|
||||||
won't start reading uninitialized or unallocated memory. In fact, BTreeMap
|
|
||||||
manages to not actually lose any of your data. When the map is dropped, all the
|
|
||||||
destructors will be successfully called! Hooray!
|
|
||||||
|
|
||||||
However BTreeMap is implemented using a modest spoonful of Unsafe Rust (most collections
|
|
||||||
are). That means that it's not necessarily *trivially true* that a bad Ord
|
|
||||||
implementation will make BTreeMap behave safely. BTreeMap must be sure not to rely
|
|
||||||
on Ord *where safety is at stake*. Ord is provided by safe code, and safety is not
|
|
||||||
safe code's responsibility to uphold.
|
|
||||||
|
|
||||||
But wouldn't it be grand if there was some way for Unsafe to trust some trait
|
|
||||||
contracts *somewhere*? This is the problem that unsafe traits tackle: by marking
|
|
||||||
*the trait itself* as unsafe to implement, unsafe code can trust the implementation
|
|
||||||
to uphold the trait's contract. Although the trait implementation may be
|
|
||||||
incorrect in arbitrary other ways.
|
|
||||||
|
|
||||||
For instance, given a hypothetical UnsafeOrd trait, this is technically a valid
|
|
||||||
implementation:
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
# use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
# struct MyType;
|
|
||||||
# unsafe trait UnsafeOrd { fn cmp(&self, other: &Self) -> Ordering; }
|
unsafe trait UnsafeOrd {
|
||||||
unsafe impl UnsafeOrd for MyType {
|
fn cmp(&self, other: &Self) -> Ordering;
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
Ordering::Equal
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
But it's probably not the implementation you want.
|
Then, a type would use `unsafe` to implement `UnsafeOrd`, indicating that
|
||||||
|
they've ensured their implementation maintains whatever contracts the
|
||||||
Rust has traditionally avoided making traits unsafe because it makes Unsafe
|
trait expects. In this situation, the Unsafe Rust in the internals of
|
||||||
pervasive, which is not desirable. The reason Send and Sync are unsafe is because thread
|
`BTreeMap` could trust that the key type's `UnsafeOrd` implementation is
|
||||||
safety is a *fundamental property* that unsafe code cannot possibly hope to defend
|
correct. If it isn't, it's the fault of the unsafe trait implementation
|
||||||
against in the same way it would defend against a bad Ord implementation. The
|
code, which is consistent with Rust's safety guarantees.
|
||||||
only way to possibly defend against thread-unsafety would be to *not use
|
|
||||||
threading at all*. Making every load and store atomic isn't even sufficient,
|
The decision of whether to mark a trait `unsafe` is an API design choice.
|
||||||
because it's possible for complex invariants to exist between disjoint locations
|
Rust has traditionally avoided marking traits unsafe because it makes Unsafe
|
||||||
in memory. For instance, the pointer and capacity of a Vec must be in sync.
|
Rust pervasive, which is not desirable. `Send` and `Sync` are marked unsafe
|
||||||
|
because thread safety is a *fundamental property* that unsafe code can't
|
||||||
Even concurrent paradigms that are traditionally regarded as Totally Safe like
|
possibly hope to defend against in the way it could defend against a bad
|
||||||
message passing implicitly rely on some notion of thread safety -- are you
|
`Ord` implementation. The decision of whether to mark your own traits `unsafe`
|
||||||
really message-passing if you pass a pointer? Send and Sync therefore require
|
depends on the same sort of consideration. If `unsafe` code cannot reasonably
|
||||||
some fundamental level of trust that Safe code can't provide, so they must be
|
expect to defend against a bad implementation of the trait, then marking the
|
||||||
unsafe to implement. To help obviate the pervasive unsafety that this would
|
trait `unsafe` is a reasonable choice.
|
||||||
introduce, Send (resp. Sync) is automatically derived for all types composed only
|
|
||||||
of Send (resp. Sync) values. 99% of types are Send and Sync, and 99% of those
|
As an aside, while `Send` and `Sync` are `unsafe` traits, they are
|
||||||
never actually say it (the remaining 1% is overwhelmingly synchronization
|
automatically implemented for types when such derivations are provably safe
|
||||||
primitives).
|
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`.
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
[drop flags]: drop-flags.html
|
[drop flags]: drop-flags.html
|
||||||
[conversions]: conversions.html
|
[conversions]: conversions.html
|
||||||
|
|
||||||
|
Loading…
Reference in new issue