mirror of https://github.com/rust-lang/nomicon
parent
03b2f6be59
commit
fad7507d4e
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,3 +1,254 @@
|
|||||||
# The Unsafe Rust Programming Language (Book)
|
% The Unsafe Rust Programming Language
|
||||||
|
|
||||||
|
**This document is about advanced functionality and low-level development practices
|
||||||
|
in the Rust Programming Language. Most of the things discussed won't matter
|
||||||
|
to the average Rust programmer. However if you wish to correctly write unsafe
|
||||||
|
code in Rust, this text contains invaluable information.**
|
||||||
|
|
||||||
|
This document seeks to complement [The Rust Programming Language Book][trpl] (TRPL).
|
||||||
|
Where TRPL introduces the language and teaches the basics, TURPL dives deep into
|
||||||
|
the specification of the language, and all the nasty bits necessary to write
|
||||||
|
Unsafe Rust. TURPL does not assume you have read TRPL, but does assume you know
|
||||||
|
the basics of the language and systems programming. We will not explain the
|
||||||
|
stack or heap, we will not explain the syntax.
|
||||||
|
|
||||||
|
|
||||||
|
# A Tale Of Two Languages
|
||||||
|
|
||||||
|
Rust can be thought of as two different languages: Safe Rust, and Unsafe Rust.
|
||||||
|
Any time someone opines the guarantees of Rust, they are almost surely talking about
|
||||||
|
Safe Rust. However Safe Rust is not sufficient to write every program. For that,
|
||||||
|
we need the Unsafe Rust superset.
|
||||||
|
|
||||||
|
Most fundamentally, writing bindings to other languages
|
||||||
|
(such as the C exposed by your operating system) is never going to be safe. Rust
|
||||||
|
can't control what other languages do to program execution! However Unsafe Rust is
|
||||||
|
also necessary to construct fundamental abstractions where the type system is not
|
||||||
|
sufficient to automatically prove what you're doing is sound.
|
||||||
|
|
||||||
|
Indeed, the Rust standard library is implemented in Rust, and it makes substantial
|
||||||
|
use of Unsafe Rust for implementing IO, memory allocation, collections,
|
||||||
|
synchronization, and other low-level computational primitives.
|
||||||
|
|
||||||
|
Upon hearing this, many wonder why they would not simply just use C or C++ in place of
|
||||||
|
Rust (or just use a "real" safe language). If we're going to do unsafe things, why not
|
||||||
|
lean on these much more established languages?
|
||||||
|
|
||||||
|
The most important difference between C++ and Rust is a matter of defaults:
|
||||||
|
Rust is 100% safe by default. Even when you *opt out* of safety in Rust, it is a modular
|
||||||
|
action. In deciding to work with unchecked uninitialized memory, this does not
|
||||||
|
suddenly make dangling or null pointers a problem. When using unchecked indexing on `x`,
|
||||||
|
one does not have to suddenly worry about indexing out of bounds on `y`.
|
||||||
|
C and C++, by contrast, have pervasive unsafety baked into the language. Even the
|
||||||
|
modern best practices like `unique_ptr` have various safety pitfalls.
|
||||||
|
|
||||||
|
It should also be noted that writing Unsafe Rust should be regarded as an exceptional
|
||||||
|
action. Unsafe Rust is often the domain of *fundamental libraries*. Anything that needs
|
||||||
|
to make FFI bindings or define core abstractions. These fundamental libraries then expose
|
||||||
|
a *safe* interface for intermediate libraries and applications to build upon. And these
|
||||||
|
safe interfaces make an important promise: if your application segfaults, it's not your
|
||||||
|
fault. *They* have a bug.
|
||||||
|
|
||||||
|
And really, how is that different from *any* safe language? Python, Ruby, and Java libraries
|
||||||
|
can internally do all sorts of nasty things. The languages themselves are no
|
||||||
|
different. Safe languages regularly have bugs that cause critical vulnerabilities.
|
||||||
|
The fact that Rust is written with a healthy spoonful of Unsafe Rust is no different.
|
||||||
|
However it *does* mean that Rust doesn't need to fall back to the pervasive unsafety of
|
||||||
|
C to do the nasty things that need to get done.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# What does `unsafe` mean?
|
||||||
|
|
||||||
|
Rust tries to model memory safety through the `unsafe` keyword. Interestingly,
|
||||||
|
the meaning of `unsafe` largely revolves around what
|
||||||
|
its *absence* means. If the `unsafe` keyword is absent from a program, it should
|
||||||
|
not be possible to violate memory safety under *any* conditions. The presence
|
||||||
|
of `unsafe` means that there are conditions under which this code *could*
|
||||||
|
violate memory safety.
|
||||||
|
|
||||||
|
To be more concrete, Rust cares about preventing the following things:
|
||||||
|
|
||||||
|
* Dereferencing null/dangling pointers
|
||||||
|
* Reading uninitialized memory
|
||||||
|
* Breaking the pointer aliasing rules (TBD) (llvm rules + noalias on &mut and & w/o UnsafeCell)
|
||||||
|
* Invoking Undefined Behaviour (in e.g. compiler intrinsics)
|
||||||
|
* Producing invalid primitive values:
|
||||||
|
* dangling/null references
|
||||||
|
* a `bool` that isn't 0 or 1
|
||||||
|
* an undefined `enum` discriminant
|
||||||
|
* a `char` larger than char::MAX
|
||||||
|
* A non-utf8 `str`
|
||||||
|
* Unwinding into an FFI function
|
||||||
|
* Causing a data race
|
||||||
|
|
||||||
|
That's it. That's all the Undefined Behaviour in Rust. Libraries are free to
|
||||||
|
declare arbitrary requirements if they could transitively cause memory safety
|
||||||
|
issues, but it all boils down to the above actions. Rust is otherwise
|
||||||
|
quite permisive with respect to other dubious operations. Rust considers it
|
||||||
|
"safe" to:
|
||||||
|
|
||||||
|
* Deadlock
|
||||||
|
* Leak memory
|
||||||
|
* Fail to call destructors
|
||||||
|
* Access private fields
|
||||||
|
* Overflow integers
|
||||||
|
* Delete the production database
|
||||||
|
|
||||||
|
However any program that does such a thing is *probably* incorrect. Rust just isn't
|
||||||
|
interested in modeling these problems, as they are much harder to prevent in general,
|
||||||
|
and it's literally impossible to prevent incorrect programs from getting written.
|
||||||
|
|
||||||
|
There are several places `unsafe` can appear in Rust today, which can largely be
|
||||||
|
grouped into two categories:
|
||||||
|
|
||||||
|
* There are unchecked contracts here. To declare you understand this, I require
|
||||||
|
you to write `unsafe` elsewhere:
|
||||||
|
* On functions, `unsafe` is declaring the function to be unsafe to call. Users
|
||||||
|
of the function must check the documentation to determine what this means,
|
||||||
|
and then have to write `unsafe` somewhere to identify that they're aware of
|
||||||
|
the danger.
|
||||||
|
* On trait declarations, `unsafe` is declaring that *implementing* the trait
|
||||||
|
is an unsafe operation, as it has contracts that other unsafe code is free to
|
||||||
|
trust blindly.
|
||||||
|
|
||||||
|
* I am declaring that I have, to the best of my knowledge, adhered to the
|
||||||
|
unchecked contracts:
|
||||||
|
* On trait implementations, `unsafe` is declaring that the contract of the
|
||||||
|
`unsafe` trait has been upheld.
|
||||||
|
* On blocks, `unsafe` is declaring any unsafety from an unsafe
|
||||||
|
operation within to be handled, and therefore the parent function is safe.
|
||||||
|
|
||||||
|
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
|
||||||
|
destructors for details.
|
||||||
|
|
||||||
|
Some examples of unsafe functions:
|
||||||
|
|
||||||
|
* `slice::get_unchecked` will perform unchecked indexing, allowing memory
|
||||||
|
safety to be freely violated.
|
||||||
|
* `ptr::offset` in an intrinsic that invokes Undefined Behaviour if it is
|
||||||
|
not "in bounds" as defined by LLVM (see the lifetimes section for details).
|
||||||
|
* `mem::transmute` reinterprets some value as having the given type,
|
||||||
|
bypassing type safety in arbitrary ways. (see the conversions section for details)
|
||||||
|
* 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. (see the FFI section for details)
|
||||||
|
|
||||||
|
As of Rust 1.0 there are exactly two unsafe traits:
|
||||||
|
|
||||||
|
* `Send` is a marker trait (it has no actual API) that promises implementors
|
||||||
|
are safe to send to another thread.
|
||||||
|
* `Sync` is a marker trait that promises that threads can safely share
|
||||||
|
implementors through a shared reference.
|
||||||
|
|
||||||
|
All other traits that declare any kind of contract *really* can't be trusted
|
||||||
|
to adhere to their contract when memory-safety is at stake. For instance Rust has
|
||||||
|
`PartialOrd` and `Ord` to differentiate between types which can "just" be
|
||||||
|
compared and those that implement a total ordering. However you can't actually
|
||||||
|
trust an implementor of `Ord` to actually provide a total ordering if failing to
|
||||||
|
do so causes you to e.g. index out of bounds. But if it just makes your program
|
||||||
|
do a stupid thing, then it's "fine" to rely on `Ord`.
|
||||||
|
|
||||||
|
The reason this is the case is that `Ord` is safe to implement, and it should be
|
||||||
|
impossible for bad *safe* code to violate memory safety. Rust has traditionally
|
||||||
|
avoided making traits unsafe because it makes `unsafe` pervasive in the language,
|
||||||
|
which is not desirable. The only reason `Send` and `Sync` are unsafe is because
|
||||||
|
thread safety is a sort of fundamental thing that a program can't really guard
|
||||||
|
against locally (even by-value message passing still requires a notion Send).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Working with unsafe
|
||||||
|
|
||||||
|
Rust generally only gives us the tools to talk about safety in a scoped and
|
||||||
|
binary manner. Unfortunately reality is significantly more complicated than that.
|
||||||
|
For instance, consider the following toy function:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn do_idx(idx: usize, arr: &[u8]) -> Option<u8> {
|
||||||
|
if idx < arr.len() {
|
||||||
|
unsafe {
|
||||||
|
Some(*arr.get_unchecked(idx))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Clearly, this function is safe. We check that the index is in bounds, and if it
|
||||||
|
is, index into the array in an unchecked manner. But even in such a trivial
|
||||||
|
function, the scope of the unsafe block is questionable. Consider changing the
|
||||||
|
`<` to a `<=`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn do_idx(idx: usize, arr: &[u8]) -> Option<u8> {
|
||||||
|
if idx <= arr.len() {
|
||||||
|
unsafe {
|
||||||
|
Some(*arr.get_unchecked(idx))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This program is now unsound, an yet *we only modified safe code*. This is the
|
||||||
|
fundamental problem of safety: it's non-local. The soundness of our unsafe
|
||||||
|
operations necessarily depends on the state established by "safe" operations.
|
||||||
|
Although safety *is* modular (we *still* don't need to worry about about
|
||||||
|
unrelated safety issues like uninitialized memory), it quickly contaminates the
|
||||||
|
surrounding code.
|
||||||
|
|
||||||
|
Trickier than that is when we get into actual statefulness. Consider a simple
|
||||||
|
implementation of `Vec`:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Note this defintion is insufficient. See the section on lifetimes.
|
||||||
|
struct Vec<T> {
|
||||||
|
ptr: *mut T,
|
||||||
|
len: usize,
|
||||||
|
cap: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note this implementation does not correctly handle zero-sized types.
|
||||||
|
// We currently live in a nice imaginary world of only positive fixed-size
|
||||||
|
// types.
|
||||||
|
impl<T> Vec<T> {
|
||||||
|
fn push(&mut self, elem: T) {
|
||||||
|
if self.len == self.cap {
|
||||||
|
// not important for this example
|
||||||
|
self.reallocate();
|
||||||
|
}
|
||||||
|
unsafe {
|
||||||
|
ptr::write(self.ptr.offset(len as isize), elem);
|
||||||
|
self.len += 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code is simple enough to reasonably audit and verify. Now consider
|
||||||
|
adding the following method:
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn make_room(&mut self) {
|
||||||
|
// grow the capacity
|
||||||
|
self.cap += 1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This code is safe, but it is also completely unsound. Changing the capacity
|
||||||
|
violates the invariants of Vec (that `cap` reflects the allocated space in the
|
||||||
|
Vec). This is not something the rest of `Vec` can guard against. It *has* to
|
||||||
|
trust the capacity field because there's no way to verify it.
|
||||||
|
|
||||||
|
`unsafe` does more than pollute a whole function: it pollutes a whole *module*.
|
||||||
|
Generally, the only bullet-proof way to limit the scope of unsafe code is at the
|
||||||
|
module boundary with privacy.
|
||||||
|
|
||||||
|
[trpl]: https://doc.rust-lang.org/book/
|
||||||
|
|
||||||
[Start at the intro](http://www.cglab.ca/~abeinges/blah/turpl/intro.html)
|
|
@ -0,0 +1,9 @@
|
|||||||
|
# Summary
|
||||||
|
|
||||||
|
* [Data Layout](data.md)
|
||||||
|
* [Ownership and Lifetimes](lifetimes.md)
|
||||||
|
* [Conversions](conversions.md)
|
||||||
|
* [Uninitialized Memory](uninitialized.md)
|
||||||
|
* [Ownership-oriented resource management (RAII)](raii.md)
|
||||||
|
* [Concurrency](concurrency.md)
|
||||||
|
* [Example: Implementing Vec](vec.md)
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -1,267 +0,0 @@
|
|||||||
% The Unsafe Rust Programming Language
|
|
||||||
|
|
||||||
**This document is about advanced functionality and low-level development practices
|
|
||||||
in the Rust Programming Language. Most of the things discussed won't matter
|
|
||||||
to the average Rust programmer. However if you wish to correctly write unsafe
|
|
||||||
code in Rust, this text contains invaluable information.**
|
|
||||||
|
|
||||||
This document seeks to complement [The Rust Programming Language Book][] (TRPL).
|
|
||||||
Where TRPL introduces the language and teaches the basics, TURPL dives deep into
|
|
||||||
the specification of the language, and all the nasty bits necessary to write
|
|
||||||
Unsafe Rust. TURPL does not assume you have read TRPL, but does assume you know
|
|
||||||
the basics of the language and systems programming. We will not explain the
|
|
||||||
stack or heap, we will not explain the syntax.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Chapters
|
|
||||||
|
|
||||||
* [Data Layout](data.html)
|
|
||||||
* [Ownership and Lifetimes](lifetimes.html)
|
|
||||||
* [Conversions](conversions.html)
|
|
||||||
* [Uninitialized Memory](uninitialized.html)
|
|
||||||
* [Ownership-oriented resource management (RAII)](raii.html)
|
|
||||||
* [Concurrency](concurrency.html)
|
|
||||||
* [Example: Implementing Vec](vec.html)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# A Tale Of Two Languages
|
|
||||||
|
|
||||||
Rust can be thought of as two different languages: Safe Rust, and Unsafe Rust.
|
|
||||||
Any time someone opines the guarantees of Rust, they are almost surely talking about
|
|
||||||
Safe Rust. However Safe Rust is not sufficient to write every program. For that,
|
|
||||||
we need the Unsafe Rust superset.
|
|
||||||
|
|
||||||
Most fundamentally, writing bindings to other languages
|
|
||||||
(such as the C exposed by your operating system) is never going to be safe. Rust
|
|
||||||
can't control what other languages do to program execution! However Unsafe Rust is
|
|
||||||
also necessary to construct fundamental abstractions where the type system is not
|
|
||||||
sufficient to automatically prove what you're doing is sound.
|
|
||||||
|
|
||||||
Indeed, the Rust standard library is implemented in Rust, and it makes substantial
|
|
||||||
use of Unsafe Rust for implementing IO, memory allocation, collections,
|
|
||||||
synchronization, and other low-level computational primitives.
|
|
||||||
|
|
||||||
Upon hearing this, many wonder why they would not simply just use C or C++ in place of
|
|
||||||
Rust (or just use a "real" safe language). If we're going to do unsafe things, why not
|
|
||||||
lean on these much more established languages?
|
|
||||||
|
|
||||||
The most important difference between C++ and Rust is a matter of defaults:
|
|
||||||
Rust is 100% safe by default. Even when you *opt out* of safety in Rust, it is a modular
|
|
||||||
action. In deciding to work with unchecked uninitialized memory, this does not
|
|
||||||
suddenly make dangling or null pointers a problem. When using unchecked indexing on `x`,
|
|
||||||
one does not have to suddenly worry about indexing out of bounds on `y`.
|
|
||||||
C and C++, by contrast, have pervasive unsafety baked into the language. Even the
|
|
||||||
modern best practices like `unique_ptr` have various safety pitfalls.
|
|
||||||
|
|
||||||
It should also be noted that writing Unsafe Rust should be regarded as an exceptional
|
|
||||||
action. Unsafe Rust is often the domain of *fundamental libraries*. Anything that needs
|
|
||||||
to make FFI bindings or define core abstractions. These fundamental libraries then expose
|
|
||||||
a *safe* interface for intermediate libraries and applications to build upon. And these
|
|
||||||
safe interfaces make an important promise: if your application segfaults, it's not your
|
|
||||||
fault. *They* have a bug.
|
|
||||||
|
|
||||||
And really, how is that different from *any* safe language? Python, Ruby, and Java libraries
|
|
||||||
can internally do all sorts of nasty things. The languages themselves are no
|
|
||||||
different. Safe languages regularly have bugs that cause critical vulnerabilities.
|
|
||||||
The fact that Rust is written with a healthy spoonful of Unsafe Rust is no different.
|
|
||||||
However it *does* mean that Rust doesn't need to fall back to the pervasive unsafety of
|
|
||||||
C to do the nasty things that need to get done.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# What does `unsafe` mean?
|
|
||||||
|
|
||||||
Rust tries to model memory safety through the `unsafe` keyword. Interestingly,
|
|
||||||
the meaning of `unsafe` largely revolves around what
|
|
||||||
its *absence* means. If the `unsafe` keyword is absent from a program, it should
|
|
||||||
not be possible to violate memory safety under *any* conditions. The presence
|
|
||||||
of `unsafe` means that there are conditions under which this code *could*
|
|
||||||
violate memory safety.
|
|
||||||
|
|
||||||
To be more concrete, Rust cares about preventing the following things:
|
|
||||||
|
|
||||||
* Dereferencing null/dangling pointers
|
|
||||||
* Reading uninitialized memory
|
|
||||||
* Breaking the pointer aliasing rules (TBD) (llvm rules + noalias on &mut and & w/o UnsafeCell)
|
|
||||||
* Invoking Undefined Behaviour (in e.g. compiler intrinsics)
|
|
||||||
* Producing invalid primitive values:
|
|
||||||
* dangling/null references
|
|
||||||
* a `bool` that isn't 0 or 1
|
|
||||||
* an undefined `enum` discriminant
|
|
||||||
* a `char` larger than char::MAX
|
|
||||||
* A non-utf8 `str`
|
|
||||||
* Unwinding into an FFI function
|
|
||||||
* Causing a data race
|
|
||||||
|
|
||||||
That's it. That's all the Undefined Behaviour in Rust. Libraries are free to
|
|
||||||
declare arbitrary requirements if they could transitively cause memory safety
|
|
||||||
issues, but it all boils down to the above actions. Rust is otherwise
|
|
||||||
quite permisive with respect to other dubious operations. Rust considers it
|
|
||||||
"safe" to:
|
|
||||||
|
|
||||||
* Deadlock
|
|
||||||
* Leak memory
|
|
||||||
* Fail to call destructors
|
|
||||||
* Access private fields
|
|
||||||
* Overflow integers
|
|
||||||
* Delete the production database
|
|
||||||
|
|
||||||
However any program that does such a thing is *probably* incorrect. Rust just isn't
|
|
||||||
interested in modeling these problems, as they are much harder to prevent in general,
|
|
||||||
and it's literally impossible to prevent incorrect programs from getting written.
|
|
||||||
|
|
||||||
There are several places `unsafe` can appear in Rust today, which can largely be
|
|
||||||
grouped into two categories:
|
|
||||||
|
|
||||||
* There are unchecked contracts here. To declare you understand this, I require
|
|
||||||
you to write `unsafe` elsewhere:
|
|
||||||
* On functions, `unsafe` is declaring the function to be unsafe to call. Users
|
|
||||||
of the function must check the documentation to determine what this means,
|
|
||||||
and then have to write `unsafe` somewhere to identify that they're aware of
|
|
||||||
the danger.
|
|
||||||
* On trait declarations, `unsafe` is declaring that *implementing* the trait
|
|
||||||
is an unsafe operation, as it has contracts that other unsafe code is free to
|
|
||||||
trust blindly.
|
|
||||||
|
|
||||||
* I am declaring that I have, to the best of my knowledge, adhered to the
|
|
||||||
unchecked contracts:
|
|
||||||
* On trait implementations, `unsafe` is declaring that the contract of the
|
|
||||||
`unsafe` trait has been upheld.
|
|
||||||
* On blocks, `unsafe` is declaring any unsafety from an unsafe
|
|
||||||
operation within to be handled, and therefore the parent function is safe.
|
|
||||||
|
|
||||||
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
|
|
||||||
destructors for details.
|
|
||||||
|
|
||||||
Some examples of unsafe functions:
|
|
||||||
|
|
||||||
* `slice::get_unchecked` will perform unchecked indexing, allowing memory
|
|
||||||
safety to be freely violated.
|
|
||||||
* `ptr::offset` in an intrinsic that invokes Undefined Behaviour if it is
|
|
||||||
not "in bounds" as defined by LLVM (see the lifetimes section for details).
|
|
||||||
* `mem::transmute` reinterprets some value as having the given type,
|
|
||||||
bypassing type safety in arbitrary ways. (see the conversions section for details)
|
|
||||||
* 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. (see the FFI section for details)
|
|
||||||
|
|
||||||
As of Rust 1.0 there are exactly two unsafe traits:
|
|
||||||
|
|
||||||
* `Send` is a marker trait (it has no actual API) that promises implementors
|
|
||||||
are safe to send to another thread.
|
|
||||||
* `Sync` is a marker trait that promises that threads can safely share
|
|
||||||
implementors through a shared reference.
|
|
||||||
|
|
||||||
All other traits that declare any kind of contract *really* can't be trusted
|
|
||||||
to adhere to their contract when memory-safety is at stake. For instance Rust has
|
|
||||||
`PartialOrd` and `Ord` to differentiate between types which can "just" be
|
|
||||||
compared and those that implement a total ordering. However you can't actually
|
|
||||||
trust an implementor of `Ord` to actually provide a total ordering if failing to
|
|
||||||
do so causes you to e.g. index out of bounds. But if it just makes your program
|
|
||||||
do a stupid thing, then it's "fine" to rely on `Ord`.
|
|
||||||
|
|
||||||
The reason this is the case is that `Ord` is safe to implement, and it should be
|
|
||||||
impossible for bad *safe* code to violate memory safety. Rust has traditionally
|
|
||||||
avoided making traits unsafe because it makes `unsafe` pervasive in the language,
|
|
||||||
which is not desirable. The only reason `Send` and `Sync` are unsafe is because
|
|
||||||
thread safety is a sort of fundamental thing that a program can't really guard
|
|
||||||
against locally (even by-value message passing still requires a notion Send).
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Working with unsafe
|
|
||||||
|
|
||||||
Rust generally only gives us the tools to talk about safety in a scoped and
|
|
||||||
binary manner. Unfortunately reality is significantly more complicated than that.
|
|
||||||
For instance, consider the following toy function:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn do_idx(idx: usize, arr: &[u8]) -> Option<u8> {
|
|
||||||
if idx < arr.len() {
|
|
||||||
unsafe {
|
|
||||||
Some(*arr.get_unchecked(idx))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Clearly, this function is safe. We check that the index is in bounds, and if it
|
|
||||||
is, index into the array in an unchecked manner. But even in such a trivial
|
|
||||||
function, the scope of the unsafe block is questionable. Consider changing the
|
|
||||||
`<` to a `<=`:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn do_idx(idx: usize, arr: &[u8]) -> Option<u8> {
|
|
||||||
if idx <= arr.len() {
|
|
||||||
unsafe {
|
|
||||||
Some(*arr.get_unchecked(idx))
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This program is now unsound, an yet *we only modified safe code*. This is the
|
|
||||||
fundamental problem of safety: it's non-local. The soundness of our unsafe
|
|
||||||
operations necessarily depends on the state established by "safe" operations.
|
|
||||||
Although safety *is* modular (we *still* don't need to worry about about
|
|
||||||
unrelated safety issues like uninitialized memory), it quickly contaminates the
|
|
||||||
surrounding code.
|
|
||||||
|
|
||||||
Trickier than that is when we get into actual statefulness. Consider a simple
|
|
||||||
implementation of `Vec`:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
// Note this defintion is insufficient. See the section on lifetimes.
|
|
||||||
struct Vec<T> {
|
|
||||||
ptr: *mut T,
|
|
||||||
len: usize,
|
|
||||||
cap: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note this implementation does not correctly handle zero-sized types.
|
|
||||||
// We currently live in a nice imaginary world of only positive fixed-size
|
|
||||||
// types.
|
|
||||||
impl<T> Vec<T> {
|
|
||||||
fn push(&mut self, elem: T) {
|
|
||||||
if self.len == self.cap {
|
|
||||||
// not important for this example
|
|
||||||
self.reallocate();
|
|
||||||
}
|
|
||||||
unsafe {
|
|
||||||
ptr::write(self.ptr.offset(len as isize), elem);
|
|
||||||
self.len += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This code is simple enough to reasonably audit and verify. Now consider
|
|
||||||
adding the following method:
|
|
||||||
|
|
||||||
```rust
|
|
||||||
fn make_room(&mut self) {
|
|
||||||
// grow the capacity
|
|
||||||
self.cap += 1;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This code is safe, but it is also completely unsound. Changing the capacity
|
|
||||||
violates the invariants of Vec (that `cap` reflects the allocated space in the
|
|
||||||
Vec). This is not something the rest of `Vec` can guard against. It *has* to
|
|
||||||
trust the capacity field because there's no way to verify it.
|
|
||||||
|
|
||||||
`unsafe` does more than pollute a whole function: it pollutes a whole *module*.
|
|
||||||
Generally, the only bullet-proof way to limit the scope of unsafe code is at the
|
|
||||||
module boundary with privacy.
|
|
||||||
|
|
@ -0,0 +1,407 @@
|
|||||||
|
/**
|
||||||
|
* Copyright 2013-2014 The Rust Project Developers. See the COPYRIGHT
|
||||||
|
* file at the top-level directory of this distribution and at
|
||||||
|
* http://rust-lang.org/COPYRIGHT.
|
||||||
|
* With elements taken from Bootstrap v3.0.2 (MIT licensed).
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
||||||
|
* <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
||||||
|
* option. This file may not be copied, modified, or distributed
|
||||||
|
* except according to those terms.
|
||||||
|
*/
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Fira Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Fira Sans'), url("FiraSans-Regular.woff") format('woff');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Fira Sans';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
src: local('Fira Sans Medium'), url("FiraSans-Medium.woff") format('woff');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Serif Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Source Serif Pro'), url("SourceSerifPro-Regular.woff") format('woff');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Serif Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("Heuristica-Italic.woff") format('woff');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Serif Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: local('Source Serif Pro Bold'), url("SourceSerifPro-Bold.woff") format('woff');
|
||||||
|
}
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Code Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: local('Source Code Pro'), url("SourceCodePro-Regular.woff") format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
|
*:not(body) {
|
||||||
|
-webkit-box-sizing: border-box;
|
||||||
|
-moz-box-sizing: border-box;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* General structure */
|
||||||
|
|
||||||
|
body {
|
||||||
|
background-color: white;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 0 15px;
|
||||||
|
font-family: "Source Serif Pro", Georgia, Times, "Times New Roman", serif;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.428571429;
|
||||||
|
|
||||||
|
-webkit-font-feature-settings: "kern", "liga";
|
||||||
|
-moz-font-feature-settings: "kern", "liga";
|
||||||
|
font-feature-settings: "kern", "liga";
|
||||||
|
}
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
body {
|
||||||
|
max-width: 750px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6, nav, #versioninfo {
|
||||||
|
font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;
|
||||||
|
}
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
color: black;
|
||||||
|
font-weight: 400;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
h1, h2, h3 {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
h4, h5, h6 {
|
||||||
|
margin-top: 12px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
}
|
||||||
|
h5, h6 {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: .1em .4em;
|
||||||
|
border-bottom: 2px solid #ddd;
|
||||||
|
}
|
||||||
|
h1.title {
|
||||||
|
line-height: 1.5em;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 26px;
|
||||||
|
padding: .2em .5em;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
padding: .2em .7em;
|
||||||
|
border-bottom: 1px solid #DDE8FC;
|
||||||
|
}
|
||||||
|
h4 {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
h5 {
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
h6 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
h1 {
|
||||||
|
font-size: 36px;
|
||||||
|
}
|
||||||
|
h2 {
|
||||||
|
font-size: 30px;
|
||||||
|
}
|
||||||
|
h3 {
|
||||||
|
font-size: 26px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nav {
|
||||||
|
column-count: 2;
|
||||||
|
-moz-column-count: 2;
|
||||||
|
-webkit-column-count: 2;
|
||||||
|
font-size: 15px;
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
}
|
||||||
|
p {
|
||||||
|
margin: 0 0 1em 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
em {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
footer {
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
font-size: 14.3px;
|
||||||
|
font-style: italic;
|
||||||
|
padding-top: 5px;
|
||||||
|
margin-top: 3em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Links layout */
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #428BCA;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
a:hover, a:focus {
|
||||||
|
color: #2A6496;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
a:focus {
|
||||||
|
outline: thin dotted #333;
|
||||||
|
outline: 5px auto -webkit-focus-ring-color;
|
||||||
|
outline-offset: -2px;
|
||||||
|
}
|
||||||
|
a:hover, a:active {
|
||||||
|
outline: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 a:link, h1 a:visited, h2 a:link, h2 a:visited,
|
||||||
|
h3 a:link, h3 a:visited, h4 a:link, h4 a:visited,
|
||||||
|
h5 a:link, h5 a:visited {color: black;}
|
||||||
|
h1 a:hover, h2 a:hover, h3 a:hover, h4 a:hover,
|
||||||
|
h5 a:hover {text-decoration: none;}
|
||||||
|
|
||||||
|
/* Code */
|
||||||
|
|
||||||
|
pre, code {
|
||||||
|
font-family: "Source Code Pro", Menlo, Monaco, Consolas, "DejaVu Sans Mono", monospace;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
pre {
|
||||||
|
border-left: 2px solid #eee;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
padding: 14px;
|
||||||
|
padding-right: 0;
|
||||||
|
margin: 20px 0;
|
||||||
|
font-size: 13px;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
code {
|
||||||
|
padding: 0 2px;
|
||||||
|
color: #8D1A38;
|
||||||
|
}
|
||||||
|
pre code {
|
||||||
|
padding: 0;
|
||||||
|
font-size: inherit;
|
||||||
|
color: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
a > code {
|
||||||
|
color: #428BCA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code highlighting */
|
||||||
|
pre.rust .kw { color: #8959A8; }
|
||||||
|
pre.rust .kw-2, pre.rust .prelude-ty { color: #4271AE; }
|
||||||
|
pre.rust .number, pre.rust .string { color: #718C00; }
|
||||||
|
pre.rust .self, pre.rust .boolval, pre.rust .prelude-val,
|
||||||
|
pre.rust .attribute, pre.rust .attribute .ident { color: #C82829; }
|
||||||
|
pre.rust .comment { color: #8E908C; }
|
||||||
|
pre.rust .doccomment { color: #4D4D4C; }
|
||||||
|
pre.rust .macro, pre.rust .macro-nonterminal { color: #3E999F; }
|
||||||
|
pre.rust .lifetime { color: #B76514; }
|
||||||
|
|
||||||
|
/* The rest */
|
||||||
|
|
||||||
|
#versioninfo {
|
||||||
|
text-align: center;
|
||||||
|
margin: 0.5em;
|
||||||
|
font-size: 1.1em;
|
||||||
|
}
|
||||||
|
@media (min-width: 992px) {
|
||||||
|
#versioninfo {
|
||||||
|
font-size: 0.8em;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0px;
|
||||||
|
right: 0px;
|
||||||
|
}
|
||||||
|
.white-sticker {
|
||||||
|
background-color: #fff;
|
||||||
|
margin: 2px;
|
||||||
|
padding: 0 2px;
|
||||||
|
border-radius: .2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#versioninfo a.hash {
|
||||||
|
color: gray;
|
||||||
|
font-size: 80%;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote {
|
||||||
|
color: #000;
|
||||||
|
margin: 20px 0;
|
||||||
|
padding: 15px 20px;
|
||||||
|
background-color: #f2f7f9;
|
||||||
|
border-top: .1em solid #e5eef2;
|
||||||
|
border-bottom: .1em solid #e5eef2;
|
||||||
|
}
|
||||||
|
blockquote p {
|
||||||
|
font-size: 17px;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
blockquote p:last-child {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul, ol {
|
||||||
|
padding-left: 25px;
|
||||||
|
}
|
||||||
|
ul ul, ol ul, ul ol, ol ol {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
dl {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
dd {
|
||||||
|
margin-left: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
nav ul {
|
||||||
|
list-style-type: none;
|
||||||
|
margin: 0;
|
||||||
|
padding-left: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Only display one level of hierarchy in the TOC */
|
||||||
|
nav ul ul {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub,
|
||||||
|
sup {
|
||||||
|
font-size: 75%;
|
||||||
|
line-height: 0;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
hr {
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px solid #eeeeee;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
overflow-x: auto;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tr.odd {
|
||||||
|
background: #eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
table td,
|
||||||
|
table th {
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Code snippets */
|
||||||
|
|
||||||
|
.rusttest { display: none; }
|
||||||
|
pre.rust { position: relative; }
|
||||||
|
.test-arrow {
|
||||||
|
display: inline-block;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 10px;
|
||||||
|
font-size: 150%;
|
||||||
|
-webkit-transform: scaleX(-1);
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.unstable-feature {
|
||||||
|
border: 2px solid red;
|
||||||
|
padding: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 1170px) {
|
||||||
|
pre {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
* {
|
||||||
|
text-shadow: none !important;
|
||||||
|
color: #000 !important;
|
||||||
|
background: transparent !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
a, a:visited {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
p a[href]:after {
|
||||||
|
content: " (" attr(href) ")";
|
||||||
|
}
|
||||||
|
footer a[href]:after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
a[href^="javascript:"]:after, a[href^="#"]:after {
|
||||||
|
content: "";
|
||||||
|
}
|
||||||
|
pre, blockquote {
|
||||||
|
border: 1px solid #999;
|
||||||
|
page-break-inside: avoid;
|
||||||
|
}
|
||||||
|
@page {
|
||||||
|
margin: 2cm .5cm;
|
||||||
|
}
|
||||||
|
h1:not(.title), h2, h3 {
|
||||||
|
border-bottom: 0px none;
|
||||||
|
}
|
||||||
|
p, h2, h3 {
|
||||||
|
orphans: 3;
|
||||||
|
widows: 3;
|
||||||
|
}
|
||||||
|
h2, h3 {
|
||||||
|
page-break-after: avoid;
|
||||||
|
}
|
||||||
|
table {
|
||||||
|
border-collapse: collapse !important;
|
||||||
|
}
|
||||||
|
table td, table th {
|
||||||
|
background-color: #fff !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#keyword-table-marker + table thead { display: none; }
|
||||||
|
#keyword-table-marker + table td { border: none; }
|
||||||
|
#keyword-table-marker + table {
|
||||||
|
margin-left: 2em;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
Loading…
Reference in new issue