What does cargo check do?
Short answer: cargo check runs the compiler far enough to find every type error, borrow error and
lint warning, then stops before code generation. You get the errors without the executable. It is
faster than cargo build, but less than you have probably been told.
What it skips
Section titled “What it skips”Compiling a binary happens in two halves. The front half parses, resolves names, does type and borrow checking, and produces metadata. The back half hands that to LLVM, which generates machine code, and then the linker assembles a binary.
cargo check does the first half only. Everything that would make your program compile wrong is still
caught; only the work of producing machine code is skipped.
That matters most for dependencies. A library dependency under cargo check is reduced to a .rmeta
metadata file. Under cargo build it becomes a compiled .rlib with real machine code in it. So the
more dependencies a project has, the wider the gap gets — and the leading cost of a big Rust project
is usually compiling its dependencies, not your own crate.
Measured, on a single crate
Section titled “Measured, on a single crate”The honest version, since the usual claim is vaguer than the reality. A project of 22,844 lines across 41 modules, no dependencies, on an M-series Mac:
cargo check |
cargo build |
|
|---|---|---|
Cold (empty target/) |
0.55 s | 0.68 s |
| Warm (one file touched) | 0.16 s | 0.26 s |
Both figures warm are three runs each, and they were stable to the hundredth of a second.
So on a small project with no dependencies, check saves you a fifth of the build. If you have been
promised that it is ten times faster, that promise came from someone with a large dependency tree.
The mechanism is worth knowing precisely, because it tells you when the gap will be large for you:
count the dependencies. A project with 200 of them spends most of its build time in codegen for code
you did not write, and that is exactly the work check removes.
Using it
Section titled “Using it”cargo checkIt reports errors and warnings the same way cargo build does:
fn main() { let unused = 42; println!("still runs");}warning: unused variable: `unused` --> src/main.rs:2:9 |2 | let unused = 42; | ^^^^^^ help: if this is intentional, prefix it with an underscore: `_unused` | = note: `#[warn(unused_variables)]` on by defaultThat is the loop it belongs in: edit, cargo check, read the errors, edit again. You get the same
diagnostics as a build, faster, without leaving an executable behind that is already out of date.
For a loop that responds to file changes automatically:
cargo install cargo-watchcargo watch -x checkWhere check is not enough
Section titled “Where check is not enough”Four things cargo check will not tell you.
Link errors. A missing C library, an undefined symbol, a conflict between two copies of the same
crate — all of these happen at link time, which is after check stops. A project can pass cargo check
and fail cargo build on a linker not found error. CI must run cargo build.
Runtime behaviour. Check never runs your program. Panics, logic errors and wrong output are invisible to it.
Test code, by default. Plain cargo check covers the default targets only. Your #[cfg(test)]
modules and benches/ are not type-checked unless you ask:
cargo check --all-targetscargo check --all-targets --all-featuresThis is the command to put in a pre-commit hook. A typo inside a test function that plain
cargo check misses will still fail cargo test on CI, and if you use a feature flag
(--all-features) plain check only compiles the default feature set.
Anything generated by a build script. Build scripts and proc macros are compiled and executed by
check, because the compiler needs their output to continue. So a broken build.rs does fail. What
does not happen is any validation of what they produced beyond its shape.
The rule
Section titled “The rule”| Situation | Command |
|---|---|
| Tight edit loop | cargo check |
| Pre-commit | cargo check --all-targets |
| CI | cargo build plus cargo test |
| Before shipping | cargo build --release |
check is a fast filter, not a replacement. Nobody ships a check.
- Back to Cargo and project layout