RustBites_Safety.html
copyright © James Fawcett
Revised: 08/01/2026
copyright © James Fawcett
Revised: 08/01/2026
Rust Bites: Safety
Rust is safe by construction via compiler enforcements
Ownership rules all
Borrow checker stands its ground,
Safety without fear.
- Haiku by ChatGPT 4o
Borrow checker stands its ground,
Safety without fear.
- Haiku by ChatGPT 4o
1.0 Why Rust?
2.0 Memory Safety
- Cannot construct uninitialized references.
-
Cannot allow references to become invalid due to reallocation of memory by the referend.
For example, a vector holding a reference to its interior might reallocate to make room for additional data.
- References cannot outlive the instances they reference.
- Cannot read from or write to memory accessed by indexing beyond the size of any indexable collection.
3.0 Rust Safety Invariants
- Safe referencing - No shared mutability through references.
- Safe indexing - All collections, including native arrays, are sized, and any attempt to index outside that sized area results in a panic, e.g., an orderly shutdown of the current thread before any reads or writes can complete.
4.0 Ownership - static analysis
- Every data item has exactly one owner. That owner deallocates the data when it goes out of scope, using a drop operation similar to a C++ destructor invocation.
- Ownership can be transferred with move operations.
- Ownership can be borrowed by creating references.
- Any number of readers (immutable references) may have access to a data value simultaneously.
- Writers (mutable owner or mutable reference) get exclusive access to a value - no other readers or writers.
5.0 Non-lexical Filters - refining static analysis
- Calling a function with one or more immutable references following a mutable borrow builds successfully, since no data in the function can be mutated during invocation. The same holds for other expressions that take immutable references.
- Creating and using an immutable reference in the scope of a mutable reference builds if, and only if, the mutable reference is not used after declaration of the immutable reference.
6.0 Interior Mutability - invariant analysis at run-time
7.0 Evaluating Safety
- Rust standard types cover much of the territory between the boundaries, i.e., region #2. These types use unsafe blocks carefully vetted by the development team to be sound. Your program simply uses one or more of these types without directly using unsafe blocks. Consider the standard library's vector. Some of its operations clearly violate the scope-based rule about no shared mutability. Vector holds a reference to its internal memory but hands out a mutable reference to your program. That is safe because the vector and compiler manage change on your behalf, refusing to mutate when outstanding references exist. If curious, you can look at unsafe blocks in vector from its source documentation. We use the vector, the compiler does not complain because of vector's few small well crafted unsafe blocks, and we benefit from the library developer's expertise. You seldom know your program is walking in the valley of "hard to evaluate region #2". You just use the standard library types and all is well.
- In some of that territory, program operations do not violate the invariants, but static analysis cannot determine that, e.g., there is no simultaneous shared mutation using references because of the way the program operates in time. For this case, one may use "interior mutability", discussed in section 6.
- You may change the design to avoid violating invariants, often by using indexing instead of references, like the directed graph class mentioned above.
Productivity Tips
- Use the Rust library linked types (HashMap, linked_list, ...) instead of trying to craft your own.
- Find alternatives to custom linked data structures, as we did for the graph type discussed above. That usually involves use of vectors.
- Pass arguments to functions by reference. Passing by value moves the caller's argument into the function, making it permanently invalid in the caller's scope.
- In methods of a type you have developed, return a member value by reference. Otherwise it moves to the caller and the member becomes invalid. If returning by value matters for ergonomic reasons, return a clone by value, incurring the performance penalty of making a clone.
- For all other functions, return a locally created object by value so the data moves to the caller. That is fast, and the returned data continues to be valid. In this case, Rust will not let you return by reference, citing lifetime issues.
7.0 References:
| Reference | Description |
|---|---|
| Jon Gjengset | Considering Rust - why should you explore Rust? |
| intorust.com | Excellent discussion of ownership and safety by an expert in five short videos. |
| Temporarily opt-in to shared mutation | Blog by Alice Ryhl, a primary maintainer of the Tokio crate. |
| Rust: Unsafe in Rust: Syntactic Patterns | Interesting analysis of unsafe in crates reported by Alex Ozdemir |
| Rust: A unique perspective | Best description of Rust ownership and sharing that I've found. Check it out! |
| RustTour.pdf | Quick tour of the Rust programming language emphasizing its unique attributes. |
| Why Rust for safe systems programming - Microsoft | Microsoft is exploring Rust for system programming. |
| The Rust Book - Ownership | Ownership rules with examples and discussion |
| Rust edition-guide | Non-lexical lifetimes |
| Rust Book | RefCell<T> and the Interior Mutability Pattern |
url
url