← Back to Blog

Rust Safety Exploration: Testing Intentional Crashes

Published: July 2, 2025
Tags: rust, compiler, c

Introduction

As I've been learning Rust through browser-based books, I wanted to test whether it's possible to intentionally crash programs like in C. The result demonstrates that Rust's compiler is remarkably robust.

System Specifications

MacBook Air M2 arm64

Setup

Docker Launch and Installation

docker run -it --rm -v $(pwd):/mnt rust:latest bash
apt update && apt install -y gcc make vim build-essential

Test Categories

We'll systematically test the following aspects:

Test Case C Language Rust
Access to freed memory ❌ Crashes/undefined behavior ✅ Compile-time prevention
Null pointer dereference ❌ Segmentation fault ✅ Option<T> system
Invalid references ❌ Undefined behavior ✅ Borrow checker
Buffer overflow ❌ Memory corruption ✅ Bounds checking

Test Results

Access to Freed Memory

C Language Example

#include <stdlib.h>
#include <stdio.h>

int main() {
    int* p = malloc(sizeof(int));
    *p = 10;
    free(p);
    printf("%d\n", *p);  // Undefined behavior!
    return 0;
}

Rust Equivalent

fn main() {
    let p = Box::new(10);
    drop(p);
    // println!("{}", *p);  // Compile error!
}

Result: Rust prevents this at compile time with ownership rules.

Null Pointer Dereference

C Language

int* p = NULL;
*p = 42;  // Segmentation fault

Rust Approach

let p: Option<i32> = None;
match p {
    Some(value) => println!("{}", value),
    None => println!("No value"),
}

Result: Rust's Option type eliminates null pointer dereferences entirely.

Invalid References

Dangling Pointer in C

int* create_dangling() {
    int x = 42;
    return &x;  // Returns address of local variable
}

Rust's Prevention

fn create_dangling() -> &i32 {
    let x = 42;
    &x  // Compile error: borrowed value does not live long enough
}

Result: Rust's borrow checker prevents dangling references at compile time.

Buffer Overflow

C Language

char buffer[10];
strcpy(buffer, "This string is way too long!");  // Buffer overflow

Rust's Safety

let mut buffer = [0u8; 10];
let data = b"This string is way too long!";
// buffer.copy_from_slice(data);  // Panic with bounds check

Result: Rust performs bounds checking and panics safely instead of corrupting memory.

Key Safety Features

Ownership System

Borrow Checker

Type System

When Rust Does Allow "Unsafe"

Rust does provide an unsafe keyword for scenarios requiring low-level control:

unsafe {
    let p = 0x12345678 as *const i32;
    println!("{}", *p);  // This CAN crash
}

However, unsafe blocks are:

Conclusion

Rust's compiler demonstrates remarkable robustness in preventing common memory safety issues that plague C programs. Through its ownership system, borrow checker, and strong type system, Rust eliminates entire classes of bugs at compile time rather than allowing them to manifest as runtime crashes.

While it's possible to write unsafe code in Rust, it requires explicit opt-in and is designed to be rare and auditable. This makes Rust an excellent choice for systems programming where both performance and safety are critical.