liveBook · Manning

Unsafe Rust

As explained in Rust in Action: Systems programming concepts and techniques

The unsafe keyword marks Rust code that may perform operations requiring guarantees the compiler cannot verify automatically.

An `unsafe` block marks code whose correctness the Rust compiler cannot verify on its own. It is a warning sign, not a claim that the code is inherently dangerous. The programmer takes responsibility for maintaining memory safety; `unsafe` does not bypass the borrow checker. section 5.1 section 3.4.1

A mutable global declared with `static mut` is one reason to use `unsafe`. An error-code example sets a global when a read fails, then checks it after the read. Both accesses appear inside `unsafe` blocks. Signal-handling code can likewise set a global flag that the main loop checks. section 3.4.1 section 12.12

Listing 3.7 Using global variables to propagate error information
 1 use rand::{random};                                  #1
 2  
 3 static mut ERROR: isize = 0;                         #2
 4  
 5 struct File;                                         #3
 6  
 7 #[allow(unused_variables)]
 8 fn read(f: &File, save_to: &mut Vec<u8>) -> usize {
 9     if random() && random() && random() {            #4
10         unsafe {
11             ERROR = 1;                               #5
12         }
13     }
14     0                                                #6
15 }
16  
17 #[allow(unused_mut)]                                 #7
18 fn main() {
19     let mut f = File;
20     let mut buffer = vec![];
21  
22     read(&f, &mut buffer);
23     unsafe {                                         #8
24         if ERROR != 0 {
25             panic!("An error has occurred!")
26         }
27     }
28 }

Follow `ERROR` from its `static mut` declaration through the unsafe write in `read` to the unsafe check in `main`. section 3.4.1

Another use is inspecting the bits beneath a value’s type. `std::mem::transmute()` reinterprets an `f32` as a `u32` without changing its bits, but it discards type-safety checks. Investigate alternatives before using it. Unsafe code is chiefly useful when interacting with external libraries and operating-system interfaces; unnecessary unsafe blocks can expose security vulnerabilities. section 5.1

Listing 5.2 Interpreting a float’s bit string as an integer
 fn main() {
   let a: f32 = 42.42;
   let frankentype: u32 = unsafe {
     std::mem::transmute(a)             #1
   };
 
   println!("{}", frankentype);         #2
   println!("{:032b}", frankentype);    #3
 
   let b: f32 = unsafe {
     std::mem::transmute(frankentype)
   };
   println!("{}", b);
   assert_eq!(a, b);                    #4
 }

See an `f32` reinterpreted as a `u32` inside `unsafe` blocks, printed as bits, then converted back and compared with the original. section 5.1

How Rust in Action: Systems programming concepts and techniques covers it

How deep it goes
Explains programmer responsibility and the borrow-checker limit through working examples, while deferring fuller treatment of the keyword.
Examples
Uses a global error flag and an `f32`-to-`u32` bit reinterpretation as concrete cases.
What it assumes
Introduces `unsafe` alongside `static mut` and explains its responsibility explicitly rather than assuming prior knowledge of the keyword.
What you can do after
Apply it via `std::mem::transmute()` to inspect a float’s bits, while recognizing that memory-safety guarantees become the programmer’s responsibility.
How it connects
Connects unsafe blocks to mutable statics, global error codes, signal handlers, type safety, and operating-system interfaces.

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 Rust in Action: Systems programming concepts and techniques. The section links point to the parts of the book it draws on.