Rust - Unsafe


The unsafe keyword allows you to create a block of code where some of Rust's safety guarantees are no longer applied. This is sometimes necessary when Rust is being to conservative.

Use cases:

  • Dereference a raw pointer
  • Call and unsafe function or method
  • Access or modify a mutable static variable
  • Implement an unsafe trait
  • Access fields of unions

It is the programmer's responsibility to make sure the code in a unsafe block is safe.

Functions

unsafe fn unsafe_func() {} // Call unsafe inside of here, user beware! Read the docs!

fn abstraction() {} // Unsafe is inside this function, but we are smarter than 
// the compiler (maybe) so we know that the contents of the function are fine

// All functions called from a different programming language are unsafe
extern "C" {
    fn abs(input: i32) -> i32;
}

fn main() {
    unsafe {
        unsafe_func();
    }

    abstraction();

    unsafe {
        println!("Abs(-12) = {}", abs(-12));
    }
}

Raw Pointers

Unsafe rust has two raw pointer types:

  • *const T
  • *mut T

Raw pointers:

  • Ignore borrowing rules
  • Do not guarantee the pointed to memory is valid
  • Can be null
  • No automatic clean-up
fn main() {
    let mut num = 4;

    // Raw pointers can be created in safe mode
    let rp1 = &num as *const i32; // *const i32 is the type
    let rp2 = &mut num as *mut i32; // *mut i32 is the type

    // Deallocating raw pointers can only be done in unsafe mode
    unsafe {
        //*rp1 = 10; // Does not work because *const
        println!("*rp1 = {}", *rp1);
        *rp2 = 12;
        println!("*rp2 = {}", *rp2);
    }
}

Static Variables

Rust static variables are called global variables in other languages.

static PI: f32 = 3.14159;
static mut COUNTER: u32 = 0;

fn main() {
    // Accessing non-mutable static variables is safe
    println!("Pi = {}", PI);

    unsafe {
        // Accessing mutable static variables is unsafe (think threads)
        COUNTER += 1;
    }
}

Traits

Traits are unsafe when at least one of its methods have some invariant that the compiler cannot verify. For example, using structs consisting of raw pointers for multi-threaded purposes. We'd have to mark the Send and Sync traits for our struct as unsafe since Rust cannot verify the struct's safety.

unsafe trait Apple {}
unsafe impl Apple for i32 {}