A segmentation fault (segfault) is a runtime error that occurs when a program tries to access a memory location it is not permitted to access — such as reading or writing outside its allocated memory bounds. The operating system catches this illegal access and terminates the offending process, sending a SIGSEGV signal on Unix-like systems or raising a STATUS_ACCESS_VIOLATION exception on Windows.

How It Happens

Program memory is divided into segments — text (instructions), data (global variables), stack (local variables), and heap (dynamically allocated memory). A segfault occurs when a reference falls outside the segment where a variable resides, or when a write is attempted to a read-only segment. The most common causes are: ​

  • Null pointer dereference — accessing memory at address 0x0

  • Buffer overflow — reading/writing past the end of an array

  • Dangling pointer — using a pointer to memory that has already been freed

  • Stack overflow — infinite recursion exhausting the stack

Rust and Segfaults

Rust’s ownership and borrow checker system is specifically designed to eliminate segfaults in safe code at compile time. According to the Rust team, a Rust program can only segfault in two scenarios: you used unsafe code that violates memory safety guarantees, or the Rust compiler itself has a bug. ​

Triggering a segfault with unsafe Rust

The most direct way is dereferencing a raw null or invalid pointer inside an unsafe block:

fn main() {
    // Dereference an invalid memory address — instant segfault
    unsafe { *(0x1 as *mut i32) = 1 };
}

This writes to memory address 0x1, which is not mapped, causing the OS to send SIGSEGV.

Stack overflow via infinite recursion

fn recurse() {
    recurse(); // infinite recursion → stack overflow → crash
}
 
fn main() {
    recurse();
}

Rust’s runtime catches this and typically raises SIGABRT with a “thread has overflowed its stack” message rather than a raw SIGSEGV, but it is the same underlying mechanism.

Writing to read-only memory via unsafe

fn main() {
    let x: &str = "hello"; // stored in read-only memory
    let ptr = x.as_ptr() as *mut u8;
    unsafe {
        *ptr = b'H'; // writing to read-only segment → segfault
    }
}

Safe Rust vs. Unsafe Rust

ScenarioSafe Rustunsafe Rust
Null pointer dereferenceImpossible — Option<T> used insteadPossible with raw pointers
Buffer overflowPanics with bounds checkPossible with raw pointer arithmetic
Dangling pointerPrevented by borrow checkerPossible
Stack overflowHandled gracefully (abort)Same behaviour

The key takeaway is that safe Rust prevents segfaults by design — the borrow checker enforces memory safety rules at compile time that languages like C/C++ leave to the programmer. When you do need low-level control, unsafe blocks opt out of these guarantees and reintroduce the risk.