Skip to main content
Rust Programming
CHAPTER 29 Beginner

Performance Optimization and Unsafe Rust

Updated: May 18, 2026
5 min read

# CHAPTER 29

Performance Optimization and Unsafe Rust

1. Chapter Introduction

By default, Rust code is incredibly fast. However, there are scenarios where you need to squeeze out every last drop of performance, or you need to interact with low-level hardware or legacy C libraries that don't obey Rust's safety rules. In this chapter, we will cover compile-time optimizations, and we will unlock the "superpowers" of the unsafe keyword.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Compile Rust code using the --release flag for maximum optimization.
  • Understand what the unsafe keyword does (and doesn't) do.
  • Dereference raw pointers.
  • Call external C functions using FFI (Foreign Function Interface).
  • Implement an unsafe trait safely.

3. The --release Flag

When you run cargo build or cargo run, Rust compiles your code in debug mode. This includes debug symbols, zero optimizations, and integer overflow checks. It compiles fast but runs slowly.

When you are ready to deploy to a production server or run a benchmark, you MUST use the release flag:

bash
1
cargo build --release

This tells the LLVM backend compiler to aggressively optimize your code (loop unrolling, inlining functions). Code compiled with --release can run 10x to 100x faster than debug code!

4. What is unsafe Rust?

The Rust compiler is conservative. If it cannot mathematically *prove* that code is safe, it rejects it. Sometimes, you (the developer) know the code is safe, but you can't prove it to the compiler. The unsafe keyword gives you four superpowers:
  1. 1. Dereference a raw pointer.
  1. 2. Call an unsafe function (or a C function).
  1. 3. Access or modify a mutable static variable.
  1. 4. Implement an unsafe trait.

Crucially: unsafe does NOT turn off the Borrow Checker! It only unlocks these four specific abilities.

5. Raw Pointers

Unlike references (&), raw pointers (*const T and *mut T) bypass safety rules:
  • They are allowed to ignore borrowing rules (you can have multiple mutable pointers to the same data).
  • They are NOT guaranteed to point to valid memory (they can be null or dangling).

You can *create* a raw pointer in safe Rust, but you can only *dereference* (read/write) it inside an unsafe block.

rust
1234567891011121314
fn main() {
    let mut num = 5;

    // Create raw pointers (Safe)
    let r1 = &num as *const i32;
    let r2 = &mut num as *mut i32;

    // Dereference raw pointers (UNSAFE!)
    unsafe {
        println!("r1 is: {}", *r1);
        *r2 = 10;
        println!("r2 is now: {}", *r2);
    }
}

6. Foreign Function Interface (FFI)

If your company has a 20-year-old math library written in C, you don't have to rewrite it. You can call it directly from Rust! Because C does not have Rust's safety guarantees, calling a C function is inherently unsafe.
rust
1234567891011
// Declare the external C function
extern "C" {
    fn abs(input: i32) -> i32;
}

fn main() {
    unsafe {
        // Calling the C function inside an unsafe block
        println!("Absolute value of -3 according to C: {}", abs(-3));
    }
}

7. Mutable Static Variables

Global variables in Rust are called static. Because multiple threads could access a mutable static variable simultaneously (causing a Data Race), reading or writing to a static mut variable is unsafe.
rust
1234567891011121314
static mut COUNTER: u32 = 0;

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

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

*(Best Practice: Avoid static mut. Use Arc<Mutex<T>> for global state instead!)*

8. Common Mistakes

  • Deploying Debug Builds: Running cargo run on a production server. Always use cargo build --release and run the binary from target/release/.
  • Abusing unsafe: Some developers get frustrated by the Borrow Checker and wrap their whole app in unsafe blocks to use C-style pointers. This defeats the entire purpose of using Rust.

9. Best Practices

  • Encapsulate unsafe: If you must use unsafe (like wrapping a C library), build a safe Rust wrapper around it. The external user of your library should only call safe Rust functions, while the unsafe code is isolated and heavily audited internally.

10. Exercises

  1. 1. Write a program that loops 10 million times and does math.
  1. 2. Run it using cargo run. Note how long it takes.
  1. 3. Run it using cargo run --release. Be amazed at the speed difference.

11. MCQs with Answers

Question 1

What flag is required to compile Rust code with full optimizations for production?

Q2. Does the unsafe keyword turn off the Borrow Checker entirely? a) Yes b) No, it only unlocks 4 specific capabilities (like dereferencing raw pointers) Answer: b) No, it only unlocks 4 specific capabilities.

Q3. Are raw pointers (*const T) guaranteed to be valid and not null? a) Yes b) No, unlike references, raw pointers can be null or dangling Answer: b) No.

Q4. Can you create a raw pointer in Safe Rust? a) Yes b) No Answer: a) Yes (You just can't dereference it outside of an unsafe block).

Question 5

What does FFI stand for?

Question 6

Calling a function written in C requires what block in Rust?

Question 7

Why is accessing a static mut (mutable global variable) considered unsafe?

Question 8

What is the recommended safe alternative to static mut for shared global state?

Q9. If you build a library that uses unsafe internally, what should you do for the user? a) Make them type unsafe to use it b) Wrap the unsafe code in a safe API so the user never has to use the unsafe keyword themselves Answer: b) Wrap it in a safe API.
Question 10

Which compiler backend does Rust use to aggressively optimize code during a release build?

12. Interview Questions

  • Q: Explain what the unsafe keyword does in Rust. Why does Rust include it if the goal of the language is safety? (Answer: Because interacting with hardware, operating systems, and C libraries inherently requires trusting the programmer over the compiler).
  • Q: What is the difference between a Reference (&) and a Raw Pointer (*const) in Rust?

13. Summary

Performance and low-level control are why Rust exists. The --release flag utilizes LLVM to generate blazingly fast machine code. The unsafe keyword acts as an escape hatch, allowing Rust developers to interface with the OS, C libraries, and raw memory, while explicitly documenting exactly where manual memory management is occurring.

14. Next Chapter Recommendation

You have completed the technical journey! In the final chapter, Chapter 30: Final Projects and Real-World Applications, we will outline architectural blueprints for several professional projects you should build to showcase your Rust expertise.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·