Advanced Level

Unsafe Rust

Chapter 19: Unsafe Rust āš ļø

Bypass Rust's safety guarantees when necessary with unsafe code.

Understanding Unsafe Rust

Unsafe Rust allows you to:

  1. Dereference raw pointers
  2. Call unsafe functions or methods
  3. Access or modify mutable static variables
  4. Implement unsafe traits
  5. Access fields of unions

Raw Pointers

fn main() {
    let mut num = 5;
    
    // Creating raw pointers
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;
    
    // Dereferencing raw pointers (unsafe)
    unsafe {
        println!("r1 is: {}", *r1);
        println!("r2 is: {}", *r2);
    }
}

Dereferencing Null Pointers

fn main() {
    let address = 0x012345usize;
    let r = address as *const i32;
    
    // This is unsafe and could cause segmentation fault
    unsafe {
        // println!("Value at address: {}", *r); // Don't do this!
    }
}

Unsafe Functions

Calling Unsafe Functions

unsafe fn dangerous() {
    // Implementation
}

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

Creating Safe Abstractions

use std::slice;

fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
    let len = slice.len();
    let ptr = slice.as_mut_ptr();
    
    assert!(mid <= len);
    
    unsafe {
        (
            slice::from_raw_parts_mut(ptr, mid),
            slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
}

fn main() {
    let mut v = vec![1, 2, 3, 4, 5, 6];
    let r = &mut v[..];
    
    let (a, b) = split_at_mut(r, 3);
    
    assert_eq!(a, &mut [1, 2, 3]);
    assert_eq!(b, &mut [4, 5, 6]);
}

External Code Integration

Calling C Functions

extern "C" {
    fn abs(input: i32) -> i32;
}

fn main() {
    unsafe {
        println!("Absolute value of -3 according to C: {}", abs(-3));
    }
}

Creating C-Compatible Functions

#[no_mangle]
pub extern "C" fn call_from_c() {
    println!("Just called a Rust function from C!");
}

// In Cargo.toml:
// [lib]
// crate-type = ["cdylib"]

Mutable Static Variables

Accessing Static Variables

static mut COUNTER: u32 = 0;

fn add_to_counter(inc: u32) {
    unsafe {
        COUNTER += inc;
    }
}

fn main() {
    add_to_counter(3);
    
    unsafe {
        println!("COUNTER: {}", COUNTER);
    }
}

Safe Static Variables

static HELLO_WORLD: &str = "Hello, world!";

fn main() {
    println!("HELLO_WORLD: {}", HELLO_WORLD);
}

Unsafe Traits

Implementing Unsafe Traits

unsafe trait Foo {
    // methods go here
}

unsafe impl Foo for i32 {
    // implementation goes here
}

Union Types

Working with Unions

union MyUnion {
    i: u32,
    f: f32,
}

fn main() {
    let u = MyUnion { i: 42 };
    
    unsafe {
        println!("u.i: {}", u.i);
        println!("u.f: {}", u.f); // Undefined behavior!
    }
}

Practical Examples

Example 1: Memory Management with Raw Pointers

use std::alloc::{alloc, dealloc, Layout};

struct MyBox<T> {
    ptr: *mut T,
    layout: Layout,
}

impl<T> MyBox<T> {
    fn new(value: T) -> MyBox<T> {
        let layout = Layout::new::<T>();
        unsafe {
            let ptr = alloc(layout) as *mut T;
            *ptr = value;
            MyBox { ptr, layout }
        }
    }
    
    fn deref(&self) -> &T {
        unsafe { &*self.ptr }
    }
}

impl<T> Drop for MyBox<T> {
    fn drop(&mut self) {
        unsafe {
            dealloc(self.ptr as *mut u8, self.layout);
        }
    }
}

fn main() {
    let my_box = MyBox::new(42);
    println!("Value: {}", my_box.deref());
    // Memory is automatically deallocated when my_box goes out of scope
}

Example 2: FFI with C Library

use std::ffi::CString;
use std::os::raw::c_char;

extern "C" {
    fn strlen(s: *const c_char) -> usize;
}

fn main() {
    let s = CString::new("Hello, world!").expect("CString::new failed");
    
    unsafe {
        let len = strlen(s.as_ptr());
        println!("Length: {}", len);
    }
}

When to Use Unsafe Code

Valid Use Cases

  1. Interfacing with C code:

    extern "C" {
        fn printf(format: *const c_char, ...) -> i32;
    }
  2. Building safe abstractions:

    fn split_at_mut(slice: &mut [i32], mid: usize) -> (&mut [i32], &mut [i32]) {
        // Implementation using unsafe code to create safe abstraction
    }
  3. Performance optimization:

    unsafe {
        // Bypassing bounds checking in performance-critical code
    }
  4. Hardware interaction:

    let port_address = 0x3f8 as *mut u8;
    unsafe {
        *port_address = 0x42; // Writing to hardware port
    }

Common Mistakes

āŒ Dereferencing Invalid Raw Pointers

fn main() {
    let mut num = 5;
    let r1 = &num as *const i32;
    let r2 = r1.wrapping_offset(1); // Points to invalid memory
    
    unsafe {
        // println!("{}", *r2); // Undefined behavior!
    }
}

āœ… Proper Raw Pointer Usage

fn main() {
    let mut arr = [1, 2, 3, 4, 5];
    let ptr = arr.as_ptr();
    
    unsafe {
        for i in 0..arr.len() {
            println!("Value at index {}: {}", i, *ptr.add(i));
        }
    }
}

āŒ Incorrect FFI Usage

extern "C" {
    fn malloc(size: usize) -> *mut u8;
    fn free(ptr: *mut u8);
}

fn main() {
    unsafe {
        let ptr = malloc(10);
        // free(ptr); // Forgot to free memory - memory leak!
    }
}

āœ… Proper FFI Memory Management

extern "C" {
    fn malloc(size: usize) -> *mut u8;
    fn free(ptr: *mut u8);
}

fn main() {
    unsafe {
        let ptr = malloc(10);
        if !ptr.is_null() {
            free(ptr); // Properly free memory
        }
    }
}

Key Takeaways

  • āœ… Unsafe code bypasses Rust's safety guarantees
  • āœ… Raw pointers can be dereferenced only in unsafe blocks
  • āœ… Unsafe functions must be called within unsafe blocks
  • āœ… Use extern "C" to interface with C code
  • āœ… Mutable static variables require unsafe access
  • āœ… Unsafe traits must be implemented with unsafe impl
  • āœ… Unions allow accessing different data types at the same memory location
  • āœ… Always prefer safe code when possible
  • āœ… Follow Rust naming conventions and idioms for unsafe code

Ready for Chapter 20? → Advanced Types

šŸ¦€ Rust Programming Tutorial

Learn from Zero to Advanced

Built with Next.js and Tailwind CSS • Open Source