How Unsafe Works
If your knowledge of Rust is limited to its syntax, you might easily assume that unsafe removes all of the compiler's famous safety guarantees. Surprisingly, this same misconception can be found in various blog posts about Rust and sometimes even in university courses. However, a diligent beginner Rust developer who has read The Rust Programming Language book already knows that unsafe allows the developer to use five precisely defined operations and does not disable other compiler features. [1]
These 5 operations are:
- Dereferencing a raw pointer
- Calling an unsafe function or method
- Accessing or modifying a mutable static variable
- Implementing an unsafe trait
- Accessing fields of a union
The developer must define an unsafe block in the code, within which these operations are possible. The restrictions of this state enable circumventing certain compiler constraints without becoming an easy escape hatch for a beginning Rust programmer.
fn main() {
// Calling an unsafe function requires an unsafe block
let x = unsafe { deref_raw_pointer() };
println!("{x}");
}
// You can use unsafe features inside an unsafe function
unsafe fn deref_raw_pointer() -> i32 {
let x = 5;
let raw = &x as *const i32;
*raw
}This means that potential memory safety issues can only occur in highly restricted areas of the code. This makes finding these bugs significantly easier both during code review and later, even if the application is already in use. Additionally, "safe" Rust behaves the same way inside an unsafe block as it does outside of it.
fn main() {
unsafe {
let mut string1 = String::from("test");
let copystring = &mut string;
/*
The borrow checker does not allow mutable and immutable
references to the same variable even in unsafe mode.
*/
println!("{string1} {copystring}");
}
}
Figure 1: Compilation error caused by simultaneous existence of mutable and immutable references.
However, the state exists for a reason, and by utilizing unsafe functions and raw pointers, it is possible to cause problems.
fn main() {
let mut s = String::from("test");
/*
By using a separate unsafe function, it's possible to create a
value pointing to the same memory location by utilizing raw pointers
*/
let copystring =
unsafe {
String::from_raw_parts(s.as_mut_ptr(), s.len(), s.capacity())
};
println!("{s} {copystring}");
}
/*
Both values are dropped and the program crashes because the values
are at the same memory address
*/
Figure 2: Double-freeing a memory location crashes the program.
use std::ptr::null;
fn main() {
// Creating a raw pointer is not an unsafe operation
let a: *const i32 = null();
// Dereferencing a raw pointer requires unsafe mode
unsafe {
// Dereferencing a null pointer causes the program to crash
println!("{}", *a);
}
}
Figure 3: Dereferencing a null pointer crashes the program.
Use Cases for Unsafe
So what is unsafe mode useful for? It's important to remember that using unsafe mode is not inherently bad, as long as it's used for a good reason.
Low-Level Programming
In my own experience, the need has arisen in embedded systems, although the ecosystem of embedded rust has improved to a point where you can now easily use ready-made libraries without having to write unsafe code yourself. Unsafe mode is also sometimes necessary in other low-level operations when dealing with memory addresses. These include, for example, device drivers and operating systems. In these scenarios, situations often arise where the Rust compiler cannot guarantee the contents of memory addresses, so unsafe mode is needed to handle them.
Foreign Function Interface
Another use case is the so-called FFI (foreign function interface), which enables interoperability with other programming languages. The Rust compiler cannot guarantee the memory safety of code written in other languages, so they require the use of unsafe mode.
Performance Optimization
Unsafe mode can also be used to eliminate performance bottlenecks. In unsafe mode, it's possible to manually perform performance optimizations that would be difficult to use in "safe" Rust. Currently, for example, using SIMD (single instruction multiple data) instructions requires unsafe mode, as the standard library's SIMD abstractions are still in an experimental phase.
Using Unsafe in Libraries
A typical way to use unsafe mode in Rust is to build a safe API around unsafe blocks. This is especially used in libraries, or crates. Library users can freely use functions whose internal implementation utilizes unsafe mode without having to use unsafe mode in their own code. This is possible because in Rust, you can use unsafe mode in a function or method that is not itself unsafe. Many libraries in Rust's ecosystem, including Rust's standard library, do use unsafe code to some extent, which may not be visible to library users at all. [2].
fn main() {
let x = raw_pointer_value(); // Does not require an unsafe block
// Print the value obtained from the unsafe function through a safe API
println!("{x}");
}
/// This function doesn't need to be unsafe
fn raw_pointer_value() -> i32 {
let a = unsafe { deref_raw_pointer() }; // Requires an unsafe block
a // function return value
}
unsafe fn deref_raw_pointer() -> i32 {
let x = 5;
let raw = &x as *const i32;
*raw
}Summary
Unsafe mode has its time and place, and should not be automatically treated as a bad thing. The strength of unsafe mode is precisely that it allows performing strictly limited operations in clearly marked areas without removing most of the compiler's safety mechanisms. This mode also must be used in certain use cases, such as low-level programming. However, it's good to remember that in most code, unsafe mode is not needed, and its excessive use is not desirable either. This is usually not a problem due to the restrictions of the mode. Automatically considering unsafe code as a bad thing and using it to scare people in various educational materials is not useful and only causes its purpose to be misunderstood.
References
1 Unsafe Rust - The Rust Programming Language --- doc.rust-lang.org. https://doc.rust-lang.org/book/ch19-01-unsafe-rust.html
2 Rust Foundation --- foundation.rust-lang.org. https://foundation.rust-lang.org/news/unsafe-rust-in-the-wild-notes-on-the-current-state-of-unsafe-rust
3 Meet Safe and Unsafe - The Rustonomicon --- doc.rust-lang.org. https://doc.rust-lang.org/nomicon/meet-safe-and-unsafe.html

