Why does `cargo build` not optimise by default?


#1

Hello!

I’ve just started learning Rust as of yesterday, so I have a lot of basic questions and I hope this is the right place to ask them?

Going through the rust-lang book and talking about Cargo, I’m told the following:

When our project is finally ready for release, we can use cargo build --release to compile our project with optimizations.

My question is; if there is a flag for optimisation, why doesn’t Cargo do this by default? It feels like I’d always want my code to be optimised?

I thought about this for a while before asking (and searched the forum for an answer, but didn’t find one), and I thought that maybe if you have a larger program, the optimise process takes a longer amount of time so you don’t want to keep doing that when you’re developing?

It’d be great to have a concrete answer from those who know more about Rust.

Thanks!


#2

Because optimising is slow, oh boy is it ever slow. Most of the time, you want to compile quickly so you can test changes, and rarely compile for actual release.

If you need the code to be optimised, even for debug builds, you can just turn optimisation on in the manifest.


#3

Optimisations get in the way of a nice compile-test/debug cycle for several reasons:

  • they take time (as @DanielKeep says)
  • they hinder debugging:
    • inlining of functions means panic! backtraces (by setting RUST_BACKTRACE=1) may not point to the actual function that caused the problem
    • more generally, it is non-trivial to preserve good debug info (as used by debuggers like gdb) in the presence of optimisation, so getting gdb to connect a certain chunk of instructions back to the code that generates it may be hard-to-impossible

Also, a release build (for Rust software but also C/C++, etc.) would generally not have any debug information at all and would often have (the most expensive/most conservative) assertions turned off. Cargo’s --release flag by default not only enables optimisations, but also disables debug info & debug assertions, making the debugging experience extremely annoying, and hiding any bugs that would be revealed by detection of, for example, integer overflow (one of the debug assertions).


#4

How does this work when someone who using my code (say, as a Crate) finds a problem in my Crate but it provides no useful debugging information?

Thanks both for some great and helpful answers.


#5

They would just compile all the code in debug mode instead. There’s really no easy way to compile your code in one profile, but your dependencies in another, and there’s no infrastructure for distributing pre-compiled crates at the moment.


#6

Ok, well thanks for all the information everyone!


#7

There’s another bit of this story that people haven’t mentioned, too.

One of the tools that you can get with cargo install is cargo-check. What it does is, it does the typechecking aspect of compiling, but doesn’t actually generate a binary out of it. Why is this good? Well, a lot of times, when you’re coding, you want the compiler to check your work, but that’s it: you’re not actually planning on running the output. And by skipping the generation, it’s much quicker.

So the idea is that, in the future, one common development methodology will be

  • code code code
  • cargo check
  • code code code
  • cargo check
  • code code code
  • okay, now I want to run it, so cargo build
  • test, go back to code/check/build loop
  • Okay, I’m ready to give to other people: cargo build --release

Fastest runs in the innermost loop, slowest ones in the outermost.