0.10Build configurations: debug and release

Last updated June 11, 2026

In lesson 0.8, your executable landed at target/debug/hello_world, and the Finished line said something about a dev profile, "unoptimized + debuginfo". Time to decode that.

When Cargo builds your program, it follows a build profile: a bundle of settings controlling how the compiler trades its time against the finished program's speed. Two profiles come standard, and they aim at opposite goals.

The dev profile

cargo build and cargo run use the dev profile (its output goes in target/debug). It's tuned for the program's author.

The compiler does no optimization: it translates your code straightforwardly, without spending effort restructuring it for speed. That keeps compiles quick, which matters because while developing, you rebuild constantly; the edit-compile-test loop is your life, and a slow loop is sand in the gears.

It also embeds debug info: bookkeeping that maps the machine code back to your source (which instruction came from which line, which memory holds which variable). Debugging tools (chapter 3) need that map to show you what a running program is doing in terms of your code rather than raw machine code.

The release profile

When the program's user matters more than its author, build with:

cargo build --release

The release profile (output: target/release) flips both settings. The compiler takes its time and optimizes aggressively, restructuring, inlining, and pruning your code into the fastest machine code it can manage, and the debug-info bookkeeping is left out. Compiles get slower; the program gets faster.

How big is the difference?

Concrete numbers, from a small computation-heavy test program (it computes a math sequence the deliberately slow way), built both ways on the author's machine, an Apple-silicon Mac. First the dev build, then the release build of the same source:

$ ./target/debug/fib_demo
fib(40) = 102334155
computed in 683.45ms

$ ./target/release/fib_demo
fib(40) = 102334155
computed in 309.48ms

Same program, same machine, same answer: the optimized build did the work in less than half the time. Meanwhile the trade ran the other way at compile time, where the release build took about two and a half times as long (0.39 s versus 0.15 s for a clean build of this tiny project; on large projects, that multiplier is the difference between sips of coffee and pots of it).

And this little benchmark is close to a best case for the dev build. It's plain arithmetic in a tight loop, the kind of code that's hard to compile badly. Programs built from Rust's higher-level conveniences (the iterators of chapter 19, say) lean on the optimizer much harder, and much larger release-versus-dev gaps are common there, precisely because optimization is what makes those conveniences cheap.

For advanced readers

For transparency, the test program. It uses features from chapters 2, 4, and 7, so treat it as a preview, not an assignment:

use std::time::Instant;

fn fib(n: u64) -> u64 {
    if n < 2 { n } else { fib(n - 1) + fib(n - 2) }
}

fn main() {
    let start = Instant::now();
    let result = fib(40);
    println!("fib(40) = {}", result);
    println!("computed in {:.2?}", start.elapsed());
}

Computing fib(40) this way calls the function more than 300 million times, so the per-call overhead the optimizer removes gets multiplied 300 million times over. That's what makes a toy this small show the gap at all.

Key insight

If optimization is so good, why not always optimize? Two reasons. Compile time: you rebuild hundreds of times a day, and dev-profile speed keeps that loop tight. And debuggability: an optimized program has been rearranged (lines fused, functions inlined, variables vanished), so when you're stepping through it hunting a bug, it no longer quite matches your source code. The dev profile keeps the machine code honest to what you wrote.

Which one, when

Best practice

Use the dev profile (plain cargo build / cargo run) for all day-to-day development. Switch to --release for two occasions: measuring how fast your program really is, and producing the final executable you'll actually use or share. Performance complaints about a debug build are filed under "self-inflicted."

One habit to start now: if a program ever feels mysteriously slow, your first question is which profile built it, long before suspecting your code or your computer. You'd be in storied company; "accidentally benchmarked the debug build" has embarrassed programmers at every level of seniority.

(For completeness: profiles are configurable: Cargo.toml can override optimization levels, debug info, and more, per profile. File that away as a thing that exists; the defaults are right for a long time.)

Next: the compiler has been quietly grading more than your errors. Let's talk about warnings.