liveBook · Manning

Unsafe Rust

As explained in Idiomatic Rust

Unsafe Rust is Rust code that permits operations whose safety conditions the compiler cannot fully verify and that therefore require the programmer to uphold the relevant invariants.

`unsafe` is an escape hatch for operations the compiler cannot fully check. It is sometimes necessary for low-level work, but it is not a way to get around the borrow checker. Scrutinize each use: the programmer must ensure it cannot cause undefined behavior. section 10.2 section 10.10

An `unsafe` block permits these operations:

  • Dereference raw pointers or call unsafe functions and methods. section 10.2.1
  • Access or modify mutable static variables, implement unsafe traits, or access union fields. section 10.2.1

Reach for unsafe when integrating C libraries or other FFI code, making system calls without safe abstractions, implementing a safe abstraction over unsafe operations, or writing a low-level optimization that cannot be expressed safely. Check for an existing safe API first: `std::ffi`, `std::fs`, and `std::process` provide examples, while `Box`, `Vec`, and `String` use unsafe operations internally. section 10.2.3 section 10.2.1

Borrowed linked-list iteration

A linked-list iterator built with `RefCell` cannot obtain the plain `&T` or `&mut T` it wants through the stable APIs described here. Its `Iter` and `IterMut` types carry a lifetime with `PhantomData` and retain a pointer to the data. Their `next()` implementations use expressions such as `unsafe { Some(&*self.data.as_ref().unwrap().as_ptr()) }` to produce references. That block permits the pointer dereference; it does not, by itself, justify the reference’s validity. section 3.2.4 section 10.2.1

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.next.clone() {
            Some(ptr) => {
                self.next = ptr.as_ref().borrow().next.clone();
                self.data = Some(ptr.as_ref().borrow().data.clone());
                unsafe { Some(&*self.data.as_ref().unwrap().as_ptr()) }
            }
            None => None,
        }
    }
}
impl<'a, T> Iterator for IterMut<'a, T> {
    type Item = &'a mut T;
    fn next(&mut self) -> Option<Self::Item> {
        match self.next.clone() {
            Some(ptr) => {
                self.next = ptr.as_ref().borrow().next.clone();
                self.data = Some(ptr.as_ref().borrow().data.clone());
                unsafe { Some(&mut *self.data.as_ref().unwrap().as_ptr()) }
            }
            None => None,
        }
    }
}

Inspect the paired `next()` implementations to compare the raw-pointer conversions used for `&T` and `&mut T`. section 3.2.4

Keep necessary unsafe code deliberate and documented. The standard library’s `Vec::insert()` is an optimization example; when writing unsafe code yourself, take extra care with correctness and use property testing, fuzz testing, and static analysis to help find bugs. Prefer a safe abstraction when one meets the need. section 10.2 section 10.2.3

How Idiomatic Rust covers it

How deep it goes
Enumerates permitted operations and examines standard-library and iterator examples, while pointing to external guidelines for further practice.
Examples
Uses `Vec::insert()` and borrowed linked-list iterators to show optimization and raw-pointer reference conversion.
What it assumes
Assumes familiarity with Rust ownership and the borrow checker while explaining which operations require `unsafe`.
What you can do after
Evaluate/understand it by identifying justified uses, rejecting borrow-checker workarounds, and checking necessary unsafe code with testing and analysis.
How it connects
Connects unsafe to raw pointers, FFI, system calls, lifetimes, the borrow checker, and safe abstractions.

Also explained in

Compare how 4 books explain unsafe Rust →

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Idiomatic Rust. The section links point to the parts of the book it draws on.