|
|
|
% Ownership and Lifetimes
|
|
|
|
|
|
|
|
Ownership is the breakout feature of Rust. It allows Rust to be completely
|
|
|
|
memory-safe and efficient, while avoiding garbage collection. Before getting
|
|
|
|
into the ownership system in detail, we will consider the motivation of this
|
|
|
|
design.
|
|
|
|
|
|
|
|
We will assume that you accept that garbage collection (GC) is not always an
|
|
|
|
optimal solution, and that it is desirable to manually manage memory in some
|
|
|
|
contexts. If you do not accept this, might I interest you in a different
|
|
|
|
language?
|
|
|
|
|
|
|
|
Regardless of your feelings on GC, it is pretty clearly a *massive* boon to
|
|
|
|
making code safe. You never have to worry about things going away *too soon*
|
|
|
|
(although whether you still wanted to be pointing at that thing is a different
|
|
|
|
issue...). This is a pervasive problem that C and C++ programs need to deal
|
|
|
|
with. Consider this simple mistake that all of us who have used a non-GC'd
|
|
|
|
language have made at one point:
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
fn as_str(data: &u32) -> &str {
|
|
|
|
// compute the string
|
|
|
|
let s = format!("{}", data);
|
|
|
|
|
|
|
|
// OH NO! We returned a reference to something that
|
|
|
|
// exists only in this function!
|
|
|
|
// Dangling pointer! Use after free! Alas!
|
|
|
|
// (this does not compile in Rust)
|
|
|
|
&s
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|
|
|
|
This is exactly what Rust's ownership system was built to solve.
|
|
|
|
Rust knows the scope in which the `&s` lives, and as such can prevent it from
|
|
|
|
escaping. However this is a simple case that even a C compiler could plausibly
|
|
|
|
catch. Things get more complicated as code gets bigger and pointers get fed through
|
|
|
|
various functions. Eventually, a C compiler will fall down and won't be able to
|
|
|
|
perform sufficient escape analysis to prove your code unsound. It will consequently
|
|
|
|
be forced to accept your program on the assumption that it is correct.
|
|
|
|
|
|
|
|
This will never happen to Rust. It's up to the programmer to prove to the
|
|
|
|
compiler that everything is sound.
|
|
|
|
|
|
|
|
Of course, Rust's story around ownership is much more complicated than just
|
|
|
|
verifying that references don't escape the scope of their referent. That's
|
|
|
|
because ensuring pointers are always valid is much more complicated than this.
|
|
|
|
For instance in this code,
|
|
|
|
|
|
|
|
```rust,ignore
|
|
|
|
let mut data = vec![1, 2, 3];
|
|
|
|
// get an internal reference
|
|
|
|
let x = &data[0];
|
|
|
|
|
|
|
|
// OH NO! `push` causes the backing storage of `data` to be reallocated.
|
|
|
|
// Dangling pointer! User after free! Alas!
|
|
|
|
// (this does not compile in Rust)
|
|
|
|
data.push(4);
|
|
|
|
|
|
|
|
println!("{}", x);
|
|
|
|
```
|
|
|
|
|
|
|
|
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
|
|
|
|
a reference into it. This is why Rust requires any references to freeze the
|
|
|
|
referent and its owners.
|
|
|
|
|
|
|
|
|