ProgrammingSystems Programming Engineer / Rust Senior Developer

What is Unsafe Rust, why is it needed, what are its rules, and how to properly use unsafe blocks to minimize risks?

Pass interviews with Hintsage AI assistant

Answer

Unsafe Rust is an extension of the safe subset of Rust that allows performing operations that the compiler cannot verify for correctness of ownership, lifetimes, and aliasing. The main use cases include interaction with low-level libraries, FFI, manual memory control, and implementing abstractions that do not fit the safe Rust model.

Key features of unsafe:

  • It requires explicit declaration of the block: unsafe { ... } or function: unsafe fn some_func()
  • Unsafe blocks allow unsafe operations: dereferencing raw pointers, calling unsafe functions and methods, accessing unions, mutable static variables, and implementing methods of unstructured memory
  • Using unsafe does not make all code in the block unsafe for the whole language — it only commits to manually guaranteeing correctness

Example:

let x: i32 = 10; let ptr: *const i32 = &x as *const i32; unsafe { println!("Value: {}", *ptr); // dereferencing raw pointer }

Trick question

Is the code inside the unsafe block completely unsafe and unchecked by the compiler, or does the compiler still apply borrow checker rules and other checks?

Answer: No, within an unsafe block, the compiler continues to check many Rust rules (such as typing, ownership rules, syntax), but allows performing only actions that would otherwise be disallowed. You cannot fully disable the borrow checker!

Example:

let mut x = 0; let r1 = &mut x as *mut i32; // Forbidden: let r2 = &mut x as *mut i32; // even inside unsafe, there will be errors if mutability is violated

Examples of real errors due to ignorance of topic nuances


Story

In an asynchronous file library, a raw pointer was used to control the buffer, but proper tracking of the buffer's lifetime was forgotten. As a result, when the file was closed, the pointer became "dangling," and access led to undefined behavior (the unsafe section did not save from use-after-free).


Story

In a custom implementation of a Vec-like structure, the programmer manually expanded the buffer via unsafe. An offset error led to incorrect copying of elements and data corruption, as alignment and layout of types were not considered.


Story

A static mutable pointer (static mut) was used in a thread descriptor without proper synchronization. As a result, a data race occurred in multi-threaded mode, leading to sporadic application crashes that were only caught during fuzzing.