| # 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,compile_fail |
| 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,compile_fail |
| 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! Use 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. |