Learn Rust With Entirely Too Many Linked Lists
29

Unsafe

unsafe is not a magic word that turns off the borrow checker. It enables exactly five operations that the compiler can't prove sound: dereferencing raw pointers, calling unsafe functions, mutating statics, implementing unsafe traits, and reading union fields. Aliasing rules, lifetime rules, and the abstract machine still apply. You're promising to uphold them yourself.

  1. The five superpowers, in canonical order: deref *mut T / *const T, call unsafe fn, access static mut, implement unsafe trait, access fields of a union. Anything else inside an unsafe block is checked exactly as it would be in safe Rust.
    unsafe {
        let p: *mut i32 = some_raw_pointer();
        let v = *p;          // 1. dereference raw pointer
        do_unsafe_thing();   // 2. call unsafe fn
        // 3. static mut
        // 4. impl unsafe trait
        // 5. union field access
    }
  2. The contract unsafe does not relax: you still cannot have a &mut T aliased by another & or &mut to the same memory at the same time. You still cannot produce a dangling reference, even briefly. Stacked borrows formalizes "at the same time" — coming up two sections from now.
    // safe code, undefined behavior:
    let mut x = 0i32;
    let r: &mut i32 = &mut x;
    let p: *mut i32 = r as *mut i32;
    unsafe {
        *p = 1;        // OK — derived from r, r still active
        *r = 2;        // OK
        *p = 3;        // UB — using p after writing through r invalidates p
    }