The Rust toolbox: rust-toolchain.toml pins, rustfmt and clippy as toolchain components, and the cargo-test-to-nextest workflow
rust-toolbox is one zero-dependency crate and the toolchain wrapped around it -- rust-toolchain.toml pinning channel and components so rustup resolves the same rustc, rustfmt, and clippy for everyone, an isolated misformatted fixture and a feature-gated clippy bait that make the format and lint gates assert real findings, and cargo test asserting the digest literal -- with committed deny.toml and clippy.toml for the cargo-nextest/cargo-deny/cargo-llvm-cov/cargo-audit tier shown as reference but absent on the host. Verified on the Fedora 44 host: verify.lua PASS 20/FAIL 0 fully offline, toolbox report digest=0x481984990deee5ff (byte-identical to ch46's C++ and ch47's Go digest), cargo fmt --check clean while rustfmt flags the fixture, cargo clippy -D warnings clean by default and naming needless_return under --features lintbait, and cargo test passing the digest unit test.
Chapter 46 pointed every C++ tool it could find at one small binary; Chapter
47 did the same for Go and found the depth had moved — out of the build
system and into go.mod’s own toolchain directives. Rust closes the arc, and
it moves the line one step further in the same direction. Go pins its
compiler in the module file; Rust pins its compiler and its formatter and
its linter, in a file rustup reads before cargo has done anything at
all. That single fact reorganizes this whole chapter. In Chapter 47,
gofumpt, golangci-lint, and staticcheck had to be gated-if-present,
because they are separate downloads that a given host may simply not have. In
Rust, rustfmt and clippy are components of the pinned channel — if the
pin resolves, they are there, at exactly the version the pin names. So the
formatter and the linter graduate out of the “shown as reference” tier and
into the hard, offline, always-runs core. toolbox (the Rust one) is a
single zero-dependency crate engineered the same way its C++ and Go siblings
were: every tool pointed at it has something concrete to catch or confirm,
never just a clean exit.
Tools used —
rustup(host; resolvesrust-toolchain.tomlbefore everycargoinvocation, gated as a hard requirement byscripts/check-host.sh’s “rustc via rustup” check, which fails outright ifrustupis missing and warns if the resolved version is not 1.97.1),cargoandrustc(host;scripts/check-host.shgates both),rustfmtandcargo fmt(host; a component of the pinned channel, not separately gated bycheck-host.shbecause pinning the channel pins it),cargo clippy(host; likewise a pinned component),cargo test(host; ships insidecargo).cargo-nextest,cargo-deny,cargo-llvm-cov, andsccacheare absent on this reference host and are not installed;cargo-audit,cargo-watch, andcargo-flamegraphare present but not exercised as gates (no advisory database offline / interactive / needsperfprivileges), so all seven of those sections below are shown as reference, not verified. No VM, no root, no LGTM stack —examples/48-rust-toolboxismode: localinexamples/manifest.yaml.
Because the Rust toolchain is itself the subject, examples/48-rust-toolbox/
ships langs: [rust] only, the same way Chapter 43’s macro chapter, Chapter
46’s C++ chapter, and Chapter 47’s Go chapter each stayed inside one
language — a C++ or Go build has nothing to say about a
rust-toolchain.toml component list or a clippy.toml MSRV. Every code
block below is a plain fenced rust/toml/console block; there is no
codetabs include to reach for when only one language is in the room.
The pin: rust-toolchain.toml
The file is nineteen lines, and only the last three do anything:
[toolchain]
channel = "1.97.1"
components = ["rustfmt", "clippy"]
channel is the part everybody knows: it names an exact release, so
cargo build in this directory compiles with 1.97.1 whatever the developer’s
rustup default happens to be. What makes the file more than a version
string is components. rustfmt and clippy are not separate projects that
happen to work with Rust — they are artifacts rustup downloads for a
specific toolchain, versioned in lockstep with it. Listing them here says
“this repository does not merely need Rust 1.97.1, it needs 1.97.1’s
formatter and 1.97.1’s linter,” and rustup will install them the first time
anything in this directory invokes cargo.
The resolution is not a convention this chapter is asserting; rustup will
tell you about it directly. Run inside examples/48-rust-toolbox/rust, on a
host whose global default is stable and which also has nightly and
1.96.0 sitting around:
$ rustup show
Default host: x86_64-unknown-linux-gnu
rustup home: /home/rsedor/.rustup
installed toolchains
--------------------
stable-x86_64-unknown-linux-gnu (default)
nightly-x86_64-unknown-linux-gnu
1.96.0-x86_64-unknown-linux-gnu
1.97.1-x86_64-unknown-linux-gnu (active)
active toolchain
----------------
name: 1.97.1-x86_64-unknown-linux-gnu
active because: overridden by '/home/rsedor/Dev/linux-systems-programming/examples/48-rust-toolbox/rust/rust-toolchain.toml'
installed targets:
x86_64-unknown-linux-gnu
active because: overridden by '…/rust-toolchain.toml' is the entire
mechanism, stated by the tool. Step one directory outside the crate and the
override evaporates:
$ cd /tmp && rustup show active-toolchain
stable-x86_64-unknown-linux-gnu (default)
That is the property Chapter 47 got from GOTOOLCHAIN and Chapter 46 had to
build by hand out of seven CMakePresets.json configurations: the version
decision lives with the source, not with whoever checked it out. What Rust
adds on top is that the tools travel with it too. Every binary in the chain
reports the same release:
$ cargo --version
cargo 1.97.1 (c980f4866 2026-06-30)
$ rustc --version
rustc 1.97.1 (8bab26f4f 2026-07-14)
$ cargo clippy --version
clippy 0.1.97 (8bab26f4f6 2026-07-14)
$ cargo fmt --version
rustfmt 1.9.0-stable (8bab26f4f6 2026-07-14)
Note the commit hashes: rustc, clippy, and rustfmt all report
8bab26f4f6 — one build of one source tree. A clippy lint’s exact wording,
a rustfmt rule’s exact output, and the compiler that accepts the result are
all the same snapshot. That is why the gates below can assert on specific
diagnostic text without being fragile: the pin holds the diagnostic still.
What is deliberately not pinned
components lists exactly two entries, and the omissions are as deliberate
as the inclusions. llvm-tools (needed by cargo-llvm-cov) and rust-src
(needed by miri and rust-analyzer) are left out, because rustup fetches any
missing component over the network, on the first cargo invocation in this
directory. Pinning a component a reader does not have turns cargo build
--offline into a download — which is exactly the failure this example exists
to avoid. The chapter’s own draft hit it: an earlier components list
included llvm-tools, and the first build in a fresh checkout printed
info: downloading component llvm-tools before compiling anything. The file
documents the opt-in instead:
# llvm-tools (needed by cargo-llvm-cov) and rust-src (needed by miri and
# rust-analyzer) are deliberately NOT pinned here: rustup fetches any missing
# component over the network on the first cargo invocation in this directory,
# which would break the offline build on a fresh host. Add them explicitly
# when you opt into those tools:
#
# rustup component add llvm-tools rust-src
That is the general rule for this file: pin what every build needs, and make everything else a documented command a reader runs on purpose.
Deterministic build and digest
Cargo.toml is short by design, and its most important section is the one
that is not there:
[package]
name = "toolbox"
version = "0.1.0"
edition = "2024"
[[bin]]
name = "toolbox"
path = "src/main.rs"
[features]
# Feature-gated clippy bait (src/lintbait.rs). Off by default so the
# default build and `cargo clippy --all-targets -- -D warnings` are clean;
# `cargo clippy --features lintbait -- -D warnings` compiles it and fires
# the stable `needless_return` lint on purpose (ch48's clippy gate).
lintbait = []
There is no [dependencies]. The crate is zero-dependency on purpose, and
cargo tree is the proof:
$ cargo tree --offline
toolbox v0.1.0 (/home/rsedor/Dev/linux-systems-programming/examples/48-rust-toolbox/rust)
One node. Cargo.lock holds exactly one package — itself. That is not
minimalism for its own sake: it is what makes --offline a meaningful flag
rather than a hopeful one. rust/demo.sh passes it on every build:
# --offline on purpose: toolbox is a zero-dependency crate, so a correct
# build never needs the network. Passing --offline turns "cargo quietly
# reached crates.io" from an invisible event into a hard build failure.
build() {
cargo build --offline --release
}
edition = "2024" is the other line worth stopping on. Editions are Rust’s
mechanism for changing language semantics without breaking old code: a 2024
crate and a 2015 crate can link together in one binary, each compiled under
its own rules. Editions are per-crate, the channel is per-repository, and
they are set in different files — Cargo.toml and rust-toolchain.toml
respectively. Both of ch48’s gates check both, because getting one right and
the other wrong is the common failure: a 2024 crate compiled by a toolchain
too old to know that edition fails with a confusing error about an unknown
edition, not an obviously-wrong one.
The payload and the hash are lifted byte-for-byte from Chapter 46’s C++ and Chapter 47’s Go toolbox, which is the whole point:
/// ch46/ch47's exact 16-byte payload (`"The quick brown."`), reused
/// byte-for-byte so the C++, Go, and Rust toolbox chapters land on the
/// identical FNV-1a digest (`0x481984990deee5ff`) -- a cross-appendix
/// easter egg, not a coincidence. FNV-1a is language-independent: any
/// conforming implementation over the same bytes produces the same 64-bit
/// value.
pub const PAYLOAD: [u8; 16] = [
0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x2e,
];
/// FNV-1a over `data`. Integer-only, no floats, addresses, timing, or
/// hash-map iteration -- the same input bytes produce the same digest
/// bit-for-bit on every conforming Rust toolchain, every architecture,
/// every run.
pub fn fnv1a(data: &[u8]) -> Digest {
let mut h = FNV_OFFSET_BASIS;
for &b in data {
h ^= b as u64;
h = h.wrapping_mul(FNV_PRIME);
}
Digest { fnv: h }
}
wrapping_mul is the one Rust-specific detail and it is load-bearing. Plain
* on u64 panics on overflow in a debug build and wraps in a release
build — so the same source would compute the digest in release and abort in
cargo test, which builds with debug assertions on. wrapping_mul states
the intent the algorithm actually requires (FNV-1a is defined modulo 2⁶⁴) and
makes the two profiles agree. C++ got this behavior by using uint64_t,
where wrapping is simply what the language does; Go got it the same way. Rust
is the only one of the three where you must ask for it, and the only one
where forgetting is caught rather than silent.
$ ./demo.sh rust build
Finished `release` profile [optimized] target(s) in 0.00s
$ ./demo.sh rust run report
toolbox report: payload_len=16 digest=0x481984990deee5ff
rustfmt: a clean tree and one isolated dirty file
A format gate that only ever asserts “the tree is clean” is half a gate: it passes just as happily when the formatter is broken, misconfigured, or looking at nothing at all. The gate needs both directions — clean where it should be clean, and loud where it should be loud — which means the example has to carry a deliberately misformatted file without that file ever affecting the build.
Rust makes the isolation easy in a way Go’s testdata/ convention and C++’s
.clang-format exclusions do not quite match: a .rs file that no mod
declaration names is not part of the crate at all. cargo build, cargo
test, cargo clippy, and cargo fmt all walk the module tree from the
crate roots, so fixtures/misformatted.rs — declared by nothing — is
invisible to every one of them:
// This file lives OUTSIDE the crate's module tree on purpose: nothing
// `mod`-declares it, so `cargo build`, `cargo test`, and `cargo clippy`
// never see it, and `cargo fmt --check` (which walks the module tree from
// the crate roots) leaves it alone. Only an explicit, per-file
// `rustfmt --edition 2024 --check fixtures/misformatted.rs` reads it -- and
// must exit nonzero, printing a diff.
//
// Do not "fix" the formatting below. The wrong spacing, the 4-into-2 indent,
// the missing trailing comma, and the crammed `if` are the assertion.
pub struct Sample{ pub id:u32, pub label:&'static str }
pub fn classify( n : i64 )->&'static str{
if n<0 {"negative"} else if n==0 {"zero"}else{"positive"}
}
rustfmt.toml sits beside it and applies to both halves of the gate:
edition = "2024"
max_width = 100
newline_style = "Unix"
use_small_heuristics = "Default"
edition = "2024" appears here as well as in Cargo.toml because rustfmt
invoked directly — as the fixture half of the gate does — has no Cargo.toml
to read. cargo fmt passes the edition through from the manifest; bare
rustfmt does not, which is why the fixture command below spells
--edition 2024 out explicitly. Two paths into the same formatter, two
different ways it learns which language version it is formatting.
The clean half is unglamorous and that is correct:
$ cargo fmt --check ; echo $?
0
No output, exit 0. The loud half, run on the same host in the same session:
$ rustfmt --edition 2024 --check fixtures/misformatted.rs
Diff in /home/rsedor/Dev/linux-systems-programming/examples/48-rust-toolbox/rust/fixtures/misformatted.rs:10:
// Do not "fix" the formatting below. The wrong spacing, the 4-into-2 indent,
// the missing trailing comma, and the crammed `if` are the assertion.
-pub struct Sample{ pub id:u32, pub label:&'static str }
+pub struct Sample {
+ pub id: u32,
+ pub label: &'static str,
+}
-pub fn classify( n : i64 )->&'static str{
- if n<0 {"negative"} else if n==0 {"zero"}else{"positive"}
+pub fn classify(n: i64) -> &'static str {
+ if n < 0 {
+ "negative"
+ } else if n == 0 {
+ "zero"
+ } else {
+ "positive"
+ }
}
Exit status 1, with a diff. verify.lua asserts three separate things about
that output — nonzero exit, a Diff in …fixtures/misformatted.rs line, and
the specific reformatted signature pub fn classify(n: i64) -> &'static str
{ — because any one of them alone could pass for the wrong reason. A nonzero
exit could be a missing file; a Diff in line could name some other file;
only the reformatted signature proves rustfmt actually parsed this source
and produced this rewrite.
clippy: clean by default, loud behind a feature
The linter gate has the same two-directional shape as the formatter’s, but the isolation problem is harder. A clippy bait is real code — it has to compile — so it cannot simply be excluded the way an unreferenced file can. Three approaches are available and two of them are wrong:
#[allow(clippy::needless_return)]on the function. This defeats the entire gate: the lint no longer fires, so there is nothing to assert.- Leave the bait in the default build, un-allowed. Then
cargo clippy --all-targets -- -D warningsfails always, and the clean half of the gate is gone. - Put the bait behind a Cargo feature, off by default. The default build and
the default clippy run never compile it;
--features lintbaitdoes.
The third is what src/lintbait.rs does, and main.rs gates both the module
declaration and the subcommand that reaches it on the same feature:
mod digest;
#[cfg(feature = "lintbait")]
mod lintbait;
The bait itself is four lines of ordinary, compiling, deliberately un-idiomatic Rust:
pub fn trip_needless_return(x: i64) -> i64 {
let y = x * 2;
return y;
}
return y; as the last statement of a function, where the bare expression
y would do. rustc accepts it without a murmur — it is valid Rust with
identical semantics either way. clippy::needless_return is a style lint,
and style lints are exactly the class of thing a compiler must not enforce
and a linter should. That division is the point of having clippy at all.
Clean first:
$ cargo clippy --offline --all-targets -- -D warnings
Checking toolbox v0.1.0 (/home/rsedor/Dev/linux-systems-programming/examples/48-rust-toolbox/rust)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.05s
--all-targets matters: without it, clippy checks the binary but skips the
#[cfg(test)] module, so lint problems in test code slip through silently.
-D warnings after the -- promotes every clippy warning to an error, which
is what turns a linter into a gate — a tool that prints warnings nobody reads
is documentation, not enforcement.
Now the same command with the feature on:
$ cargo clippy --offline --features lintbait --all-targets -- -D warnings
Checking toolbox v0.1.0 (/home/rsedor/Dev/linux-systems-programming/examples/48-rust-toolbox/rust)
error: unneeded `return` statement
--> src/lintbait.rs:13:5
|
13 | return y;
| ^^^^^^^^
|
= help: for further information visit https://rust-lang.github.io/rust-clippy/rust-1.97.0/index.html#needless_return
= note: `-D clippy::needless-return` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::needless_return)]`
help: remove `return`
|
13 - return y;
13 + y
|
error: could not compile `toolbox` (bin "toolbox") due to 1 previous error
Read the word error there: the same finding is a warning by default and an
error only because of -D warnings. The note says so outright — -D
clippy::needless-return` implied by `-D warnings. The gate asserts the lint
name (needless_return), the diagnostic text (unneeded `return`
statement), and the file (src/lintbait.rs), never a count of findings.
Counts move whenever a clippy release adds a lint; a specific stable lint’s
name and message do not, especially with the channel pinned.
clippy.toml sits beside rustfmt.toml and pins the thresholds that decide
what “clean” means:
msrv = "1.97.1"
too-many-arguments-threshold = 8
type-complexity-threshold = 250
msrv is the interesting one. It tells clippy the minimum Rust version this
crate supports, and clippy suppresses lints whose suggested fix would not
compile on that version — the linter refusing to recommend a language feature
you cannot use. With the channel pinned to the same value, MSRV and toolchain
agree by construction here, which is the easy case; in a real crate that
supports a range of versions, this line is what keeps clippy’s advice
actionable.
cargo test: the gate that catches drift first
The digest test lives in src/digest.rs, inside the file it tests, which is
Rust’s default and a genuine difference from both siblings — C++ needed a
separate translation unit and Go a separate _test.go file:
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fnv1a_matches_cross_language_digest() {
let d = fnv1a(&PAYLOAD);
assert_eq!(
d,
Digest {
fnv: 0x481984990deee5ff
},
"digest must match the C++/Go toolbox chapters' literal"
);
}
}
#[cfg(test)] means the module and everything in it are compiled out of the
release binary entirely — the test costs nothing at run time, and use
super::* reaches the private items of its parent without any of them being
made pub for testing’s sake.
$ cargo test --offline
Compiling toolbox v0.1.0 (/home/rsedor/Dev/linux-systems-programming/examples/48-rust-toolbox/rust)
Finished `test` profile [unoptimized + debuginfo] target(s) in 0.10s
Running unittests src/main.rs (target/debug/deps/toolbox-6b13b413a4fa8296)
running 1 test
test digest::tests::fnv1a_matches_cross_language_digest ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
The gate asserts the named test line and the test result: ok. 1 passed; 0
failed summary, not the process exit code — a cargo test run that compiled
zero tests also exits 0, and would sail past a weaker check.
It is worth seeing the gate fail on purpose, because a gate nobody has
watched fail is a gate nobody knows works. Flipping the payload’s last byte
from 0x2e (.) to 0x21 (!) in a scratch copy of the crate:
$ cargo test --offline
running 1 test
test digest::tests::fnv1a_matches_cross_language_digest ... FAILED
failures:
---- digest::tests::fnv1a_matches_cross_language_digest stdout ----
thread 'digest::tests::fnv1a_matches_cross_language_digest' (480627) panicked at src/digest.rs:43:9:
assertion `left == right` failed: digest must match the C++/Go toolbox chapters' literal
left: Digest { fnv: 5195334935605341822 }
right: Digest { fnv: 5195329438047200767 }
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
failures:
digest::tests::fnv1a_matches_cross_language_digest
test result: FAILED. 0 passed; 1 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
One byte, and assert_eq! prints both sides in decimal because that is how
Digest’s derived Debug formats a u64. 5195329438047200767 is
0x481984990deee5ff — the identical decimal Chapter 47’s delve gate
printed when it read the Go struct’s field out of a live process. Two
languages, two entirely different tools, one number.
This is also why the test gate is separate from the report gate rather than
redundant with it. Both assert the same literal, but they fail at different
times and say different things: cargo test fails at check time with both
values in front of you, while toolbox report fails only once someone runs
the binary and compares output. Drift gets caught in the cheaper place first.
How the code works
main.rs’s dispatch is a match on the first argument, and the lintbait
arm is compiled in only when the feature is:
fn main() {
let mut args = std::env::args().skip(1);
match args.next().as_deref() {
Some("report") => cmd_report(),
#[cfg(feature = "lintbait")]
Some("lintbait") => {
let doubled = lintbait::trip_needless_return(21);
println!("toolbox lintbait: doubled={doubled}");
}
_ => {
print_usage();
std::process::exit(2);
}
}
}
args.next().as_deref() is the idiom worth naming: std::env::args() yields
owned Strings, and matching a String against the literal "report"
requires a &str. as_deref() turns the Option<String> into an
Option<&str> without cloning, so the match arms can be plain string
literals. Attribute-on-a-match-arm is legal Rust and does exactly what it
looks like — with the feature off, that arm does not exist, and the
lintbait argument falls through to print_usage() and exit 2.
cmd_report is the deterministic half:
fn cmd_report() {
let d = fnv1a(&PAYLOAD);
println!(
"toolbox report: payload_len={} digest=0x{:016x}",
PAYLOAD.len(),
d.fnv
);
}
{:016x} — zero-padded to sixteen hex digits — is the formatting detail that
makes the output a stable string to assert on. Without the 016, a digest
whose top nibble happened to be zero would print fifteen characters and the
literal comparison would fail for a reason that has nothing to do with the
hash being wrong.
The rest of the crate is the gate scaffolding, and each piece is isolated
from the digest by a different mechanism, on purpose: src/lintbait.rs by
a Cargo feature (not compiled unless asked), fixtures/misformatted.rs by
module-tree exclusion (not part of the crate at all), and digest.rs’s test
module by #[cfg(test)] (not in the release binary). Nothing about running
toolbox report can depend on whether any designed-for finding has or has
not been “fixed.”
Errors, three ways
With one language and no second compiler to disagree with itself, “three ways” here means three surfaces reading the same source, each willing to read a different amount of context before it says anything — and, in Rust’s case, each one promoting a different severity into a failure.
The first surface is rustfmt, and it notices only shape. misformatted.rs
is not wrong in any semantic sense; classify returns the correct string for
every input. rustfmt still exits 1, because shape is the entire question it
answers, and it answers it by rewriting rather than by judging. There is no
severity to promote: a formatter’s finding is binary.
The second surface is clippy, which reads idiom — patterns the compiler
accepts and a Rust programmer would not write. return y; is the whole
example, and the interesting part is that clippy’s default verdict is
warning, not error. The severity is a policy decision the invoker makes, and
-D warnings is where that decision gets made:
= note: `-D clippy::needless-return` implied by `-D warnings`
This is a genuinely different model from the other two chapters. go vet’s
printf finding is reported at one fixed severity; a C++ compiler warning
becomes an error only via -Werror at build-configuration time. Rust splits
the linter from the compiler and then lets the caller set the threshold per
invocation — so a developer’s inner loop can run clippy at warning level and
CI can run the identical command with -D warnings and get a hard failure,
with no second configuration to keep in sync.
The third surface is cargo test, and it is the only one that runs the code.
assert_eq! does not read shape or idiom; it reads behavior, and the
failure it produces is a panic with both values printed. Nothing static could
have caught a wrong payload byte — the code is well-formatted, idiomatic, and
type-correct with 0x21 in it. Only executing fnv1a and comparing the
result finds it.
Rust also draws a line the other two chapters do not have to: Result versus
panic. toolbox has no Result in it, which is itself the design decision —
the report path has nothing that can fail (a fixed array, an infallible hash,
a println!), so introducing a Result would be inventing an error case to
have one. The panics that do appear are all in test code, where a panic is
the reporting mechanism. Chapter 5 — the book’s own “Errors, three ways” —
and Chapter 43 cover Result propagation and ? at length for code that has
real fallible I/O; a tooling chapter’s
contribution is the opposite observation — that a deterministic gate should
have no error paths to test, because every branch it does not have is a
branch that cannot drift.
Concurrency lens
The concurrency in this chapter belongs to the tools, not to toolbox,
which is strictly single-threaded. Three layers of it are worth naming, and
Rust’s tooling makes a choice at each one that differs from Go’s.
The first is the build graph. cargo parallelizes across crates by default —
-j defaults to the CPU count, 16 on this host — which is why a
zero-dependency crate builds in well under a second and a crate with a deep
dependency tree does not. That default is also why a lockfile matters for
reproducibility but not for speed: the graph’s shape is fixed by
Cargo.lock, and cargo walks it as wide as the machine allows. Inside a
single crate, rustc parallelizes differently again, by splitting the crate
into codegen units (16 in the release profile by default, 1 under
lto = "fat"), which trades compile time against optimization quality — the
same trade mold and ccache addressed from the outside in Chapter 46, made
from the inside here.
The second is the test harness. cargo test builds one test binary and
libtest runs the #[test] functions in threads, inside that one process,
as wide as the CPU count unless told otherwise:
$ cargo test --offline -- --test-threads=1
running 1 test
test digest::tests::fnv1a_matches_cross_language_digest ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
--test-threads=1 exists because that thread-per-test model has real
consequences for systems code specifically. Tests sharing a process share
errno-adjacent global state, the signal-disposition table, the file
descriptor table, the current working directory, and the process’s own
rlimits — so a test that installs a signal handler, chdirs, or exhausts
FDs can change the result of a test running concurrently beside it. Every one
of those is something the earlier parts of this book taught you to reach for.
cargo test -- --test-threads=1 is the blunt fix, at the cost of all
parallelism.
cargo-nextest is the third layer, and it is the reason it appears in this
chapter’s tool list at all: it runs each test in its own process. That
turns process-global state from a shared hazard into per-test isolation for
free, and it means a test that segfaults or abort()s takes down only itself
rather than the whole run — which for a book full of mmap, signal, and
rlimit examples is not a marginal benefit. The cost is process-spawn
overhead per test, which matters for thousands of microsecond-scale unit
tests and does not matter here. Go made the opposite structural choice —
one process per package, tests within a package sharing it — and Chapter
47’s b.RunParallel benchmark lived entirely inside that shared process. Same
problem, two different places to put the isolation boundary.
The gated-if-present tier: cargo-nextest, cargo-deny, cargo-llvm-cov, cargo-audit
None of these four ships with the toolchain; each is a cargo install. On
this reference host, cargo-nextest, cargo-deny, and cargo-llvm-cov are
absent and cargo-audit is present but has no advisory database, and this
iteration’s decision was to install nothing and fetch nothing. So all four
are shown as reference, not verified — verify.lua prints an
informational SKIP: for each rather than failing. What is committed is
the configuration, because that is the durable artifact: a reader who runs
cargo install cargo-deny gets a working gate with no further authoring.
cargo nextest run would name the test and print its own summary
(1 test run: 1 passed); the gate asserts those tokens, not an exit code.
deny.toml is the substantial one. Its offline-answerable checks — bans,
licenses, sources — are answerable from Cargo.lock alone, which is why
those three are the subsets the gate would run:
[licenses]
# toolbox is zero-dependency, so this list constrains exactly one crate:
# toolbox itself. That is the point -- the policy is in place before the
# first dependency arrives, not bolted on after.
allow = [
"MIT",
"Apache-2.0",
"Apache-2.0 WITH LLVM-exception",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"Unicode-3.0",
]
confidence-threshold = 0.93
[bans]
multiple-versions = "deny"
wildcards = "deny"
highlight = "all-duplicates"
multiple-versions = "deny" is the line that earns its place in a systems
crate: Cargo will happily link two semver-incompatible versions of the same
crate into one binary, which for a crate wrapping a C library or holding a
global registry means two independent copies of state that both think they
are the only one. wildcards = "deny" forbids version = "*" dependencies,
which is how a lockfile-clean build turns into a different build next week.
cargo deny check advisories is deliberately not in the gated subset:
it needs the RustSec database cloned into ~/.cargo/advisory-db, a network
fetch this example refuses to make.
cargo llvm-cov --summary-only is the coverage tool, and the gate that would
run it asserts a filename — that digest.rs appears in the summary —
never a percentage. A percentage moves every time a line is added to the
crate, so gating on one produces failures that have nothing to do with the
toolchain. It also needs rustup component add llvm-tools, per the pin
discussion above.
cargo audit is the network-shaped one, and it gets a double guard in
verify.lua — on the binary and on the database:
if not tool_present("cargo-audit") then
print("SKIP: cargo-audit not found on PATH -- gate H not asserted")
elseif checks.run("test -d \"$HOME/.cargo/advisory-db\"").exit ~= 0 then
Without the second check this host would pass the tool test and then have
cargo audit clone the RustSec database mid-verification — turning a
network outage into a test failure, and an offline example into an online
one. What a real finding looks like, shown here as clearly-labeled
illustrative output rather than a captured run, since this crate has no
vulnerable dependency by design and none will be committed:
Crate: some-crate
Version: 0.4.1
Title: Use-after-free in some-crate
Date: 2026-03-11
ID: RUSTSEC-2026-0031
URL: https://rustsec.org/advisories/RUSTSEC-2026-0031
Severity: 7.5 (high)
Solution: Upgrade to >=0.4.3
The durable token there is the RUSTSEC-YYYY-NNNN identifier — stable,
citable, and the thing a policy actually references. Chapter 47 made the same
call for govulncheck and for the same two reasons: network, and no
committed vulnerability.
Shown, not gated: cargo-watch, cargo-flamegraph, sccache
Three tools round out a working Rust setup and none of them is asserted, each for a different reason worth stating rather than hiding.
cargo watch -x 'clippy --all-targets -- -D warnings' re-runs the lint gate
on every save. It is the inner-loop counterpart to the CI gate — the same
command, run continuously — and it is not gated because it never terminates.
A check that never returns is not a check.
cargo flamegraph --bin toolbox -- report produces an SVG of where CPU time
went. It needs perf, which needs kernel.perf_event_paranoid lowered or
root, and it produces output that differs run to run by construction. Chapter
31 already ran cargo-flamegraph for real against a Rust binary and taught
how to read the result, and Chapter 26 hit the perf_event_paranoid=2
privilege wall head-on; the reason it is not gated here is the same reason
Chapter 46 did not gate ccache — sampling output is not a deterministic
assertion.
sccache caches compilation artifacts across builds
(RUSTC_WRAPPER=sccache). It is a pure accelerator: a correct build and a
correct cached build produce identical output, so there is nothing about
correctness to assert. Its own metric — cache hit rate — depends on what you
compiled five minutes ago, which is exactly the kind of number this book
refuses to gate on. ccache and mold got the same treatment in Chapter 46.
miri deserves a pointer rather than a section: it interprets Rust at the
MIR level and catches undefined behavior in unsafe code that no amount of
formatting, linting, or testing will. It is not re-taught here because
Chapters 29 and 43 already own it, and toolbox has no unsafe block for it
to find anything in — which is the correct reason to omit a tool, as opposed
to omitting it because it is inconvenient.
Build, run, observe
Everything below runs on the host, offline, with no cargo install and no
VM:
[host]$ cd examples/48-rust-toolbox
[host]$ ./demo.sh rust build # zero-dep, --offline, no network
[host]$ ./demo.sh rust run report # digest=0x481984990deee5ff
[host]$ cd rust
[host]$ rustup show # "active because: overridden by ...rust-toolchain.toml"
[host]$ cargo tree --offline # exactly one node
[host]$ cargo fmt --check # silent, exit 0
[host]$ rustfmt --edition 2024 --check fixtures/misformatted.rs # diff, exit 1
[host]$ cargo clippy --offline --all-targets -- -D warnings # clean
[host]$ cargo clippy --offline --features lintbait --all-targets -- -D warnings
[host]$ cargo test --offline # the named digest test
And the gate itself:
$ cd examples/48-rust-toolbox
$ LSP_LANG=rust REPO_ROOT=$(cd ../.. && pwd) lua verify.lua
ok: rust: cargo build --offline succeeds (zero-dep, no network)
ok: rust-toolchain.toml: pins channel 1.97.1
ok: rust-toolchain.toml: pins the rustfmt + clippy components
ok: Cargo.toml: sets edition 2024
ok: rust: rustc --version runs through the pin
ok: rust: the pin really resolves -- rustc reports 1.97.1
ok: rust: toolbox report runs
ok: rust: toolbox report equals the expected literal (matches ch46's C++ and ch47's Go digest)
ok: rust: cargo fmt --check exits 0 on the crate's module tree
ok: rust: cargo fmt --check prints no diff for tracked sources
ok: rust: rustfmt --check exits nonzero on the misformatted fixture
ok: rust: rustfmt --check prints a diff naming the fixture
ok: rust: the diff shows the reformatted signature rustfmt would write
ok: rust: cargo clippy --all-targets -- -D warnings is clean on the default build
ok: rust: the clean clippy run really checked the crate (cargo reports Finished)
ok: rust: clippy --features lintbait fails under -D warnings
ok: rust: clippy names the stable needless_return lint
ok: rust: clippy emits the needless_return diagnostic text
ok: rust: the finding is located in src/lintbait.rs
ok: rust: cargo test succeeds
ok: rust: the named digest test runs and passes
ok: rust: cargo test reports the summary line, not just exit 0
SKIP: cargo-nextest not found on PATH -- gate E not asserted
SKIP: cargo-deny not found on PATH -- gate F not asserted
SKIP: cargo-llvm-cov not found on PATH -- gate G not asserted
SKIP: ~/.cargo/advisory-db absent (offline; cargo-audit would fetch it) -- gate H not asserted
info: cargo-watch, cargo-flamegraph and sccache are shown in the chapter but not gated (interactive / needs perf privileges / cache-hit-rate is not a correctness property)
info: miri is not re-taught here -- ch29 and ch43 own it
PASS 20 / FAIL 0
Four SKIP: lines, no failures. That is the shape a gated-if-present tier is
supposed to have: the absent tools are named, in the output, where nobody can
mistake “not run” for “passed.”
Cross-check: the same digest, three languages apart
Three chapters, three languages, three completely unrelated toolchains, one 16-byte payload:
$ examples/46-cpp-toolbox/cpp/build/release/toolbox report
toolbox report: label=[toolbox] report payload_len=16 digest=0x481984990deee5ff
$ examples/47-go-toolbox/go/bin/toolbox report
toolbox report: payload_len=16 digest=0x481984990deee5ff
$ examples/48-rust-toolbox/rust/target/release/toolbox report
toolbox report: payload_len=16 digest=0x481984990deee5ff
(The C++ line carries an extra label=[toolbox] field from Chapter 46’s
argument-echo demo; the payload_len and digest fields are identical
across all three.)
FNV-1a is language-independent — a shift, an xor, and a multiply modulo
2⁶⁴ — so nothing about that agreement is surprising in principle. What
makes it worth printing is that agreeing in practice required each chapter to
have gotten a different language-specific detail right: C++ needed
uint64_t’s defined wrapping and a -fsanitize=undefined build that did not
trip on it, Go needed the untyped-constant rules not to promote the FNV prime
into something wider, and Rust needed wrapping_mul so a debug build would
not panic where release wraps. Three different ways to get modular arithmetic
wrong, three different tools that would have caught it, one number that says
none of them did.
Where this sits next to the book’s other tooling chapters
Part 13 covers tooling in four chapters and this is the last of them, so the arc is worth naming explicitly. Chapter 45 handled the analysis suites — tools that observe a running system from outside it. Chapters 46, 47, and 48 each took one language and pointed its whole toolchain at one small binary, and the interesting result is how differently the three languages distribute the same responsibilities:
- C++ (ch46) puts almost nothing in the language distribution. CMake,
Conan, clang-tidy, clang-format, the sanitizers, gdb, ccache, and mold are
eight separate projects with eight separate versions, and
CMakePresets.jsonexists to make “which of these, configured how” an explicit, committed choice. The chapter’s parity gate — GCC and clang producing byte-identical output — is a check the other two languages have no reason to run. - Go (ch47) puts the compiler, the formatter, the vetter, the test
runner, the profiler, and the generator inside one
gobinary, and pins that binary’s version ingo.mod. What it leaves outside — gofumpt, golangci-lint, staticcheck, delve, benchstat — is precisely what Chapter 47 had to gate as if-present. - Rust (ch48) pins a toolchain rather than a binary, and that toolchain
includes the formatter and the linter as components. The consequence is
this chapter’s structure: the hard, offline core covers formatting and
linting, which Chapter 47’s could not, while the if-present tier is a
different set of tools entirely (nextest, deny, llvm-cov, audit) — every
one of them a
cargo installrather than a component.
Chapter 31 sits underneath all three: it ran each language’s profiling
toolbelt — perf for C++, the pprof ecosystem for Go, cargo-flamegraph
and clippy for Rust — against one shared workload, which is why these three
chapters could concentrate on the build-and-check side instead of re-teaching
how to read a flamegraph.
The dependency and supply-chain story splits the same way. C++ needed Conan
lockfiles to have a dependency graph worth checking at all; Go’s module
system makes the graph free and govulncheck the tool that reads it; Rust
gets the graph free too and splits the reading in two — cargo-deny for
policy answerable from the lockfile, cargo-audit for the RustSec advisory
database over the network. Chapters 47 and 48 made the identical call about
that network half: show it, name the durable identifier, never gate it.
What you learned
rust-toolchain.tomlpins more than a compiler.channelfixes the release;componentsfixes which of that release’s artifacts get installed. Becauserustfmtandclippyare components, pinning the channel pins the formatter and the linter at the same commit asrustc— which is why this chapter’s format and lint gates are hard and offline where Chapter 47’s equivalents could not be.rustup showstates the override.active because: overridden by '…/rust-toolchain.toml'is the mechanism reported by the tool, and it disappears one directory outside the crate.- Do not pin components you do not need.
rustupfetches missing components over the network on the firstcargoinvocation, so an aspirationalllvm-toolsincomponentssilently breakscargo build --offline. Documentrustup component addinstead. - A format gate needs both directions. Clean on the tree and loud on a
known-dirty file. In Rust the dirty file isolates for free: a
.rsfile nomoddeclares is not part of the crate, so nothing but an explicit per-filerustfmtinvocation ever reads it. - A lint gate needs both directions too, and a Cargo feature is the clean
way to get them.
#[allow]on the bait destroys the gate; leaving the bait in the default build destroys the clean half.#[cfg(feature = "lintbait")]keeps both. -D warningsis where severity is decided, per invocation. Clippy’s findings are warnings by default; the caller promotes them. The inner loop and CI can run the identical command at different thresholds with no second config to keep in sync.- Assert the lint name and the diagnostic text, never a finding count. Counts move with every clippy release; a stable lint’s name does not, especially with the channel pinned.
wrapping_mul, not*. Rust panics on overflow in debug and wraps in release, so an algorithm defined modulo 2⁶⁴ has to say so — otherwisecargo testand the release binary disagree.- Gate on the summary line, not the exit code.
cargo testwith zero tests compiled also exits 0.test result: ok. 1 passed; 0 faileddoes not. cargo testruns tests in threads inside one process;cargo-nextestruns each in its own. For systems code that touches signal dispositions, the FD table, the CWD, orrlimits, that difference is the whole reason to reach for nextest — or for--test-threads=1.- Guard a network-shaped gate on the data, not just the binary.
cargo-auditpresent with no~/.cargo/advisory-dbwould silently clone the RustSec database mid-verification. Two guards, one skip. - Say which tools did not run, in the output. Four
SKIP:lines besidePASS 20 / FAIL 0is the shape that keeps “absent” from reading as “passed.”
verified — on the Fedora 44
reference host this session (kernel 7.1.5-201.fc44, rustc/cargo 1.97.1 via
rustup, clippy 0.1.97, rustfmt 1.9.0-stable, offline, no network access
used): LSP_LANG=rust REPO_ROOT=$(cd ../.. && pwd) lua verify.lua
reported PASS 20 / FAIL 0, and
python3 scripts/test-all-examples.py --only 48-rust-toolbox
reported 1 passed, 0 failed, 0 skipped. Confirmed live and
folded into that PASS count, the pinned-toolchain hard gates A-D:
rustup show reported active because: overridden by
'…/examples/48-rust-toolbox/rust/rust-toolchain.toml' while
rustup show active-toolchain outside the crate reported
stable-x86_64-unknown-linux-gnu (default);
rust-toolchain.toml pins channel = "1.97.1" with
components = ["rustfmt", "clippy"] and Cargo.toml
sets edition = "2024"; rustc --version resolved
through the pin to rustc 1.97.1 (8bab26f4f 2026-07-14);
cargo tree --offline printed exactly one node;
./demo.sh rust run report printed toolbox report:
payload_len=16 digest=0x481984990deee5ff, byte-identical to Chapter
46's C++ and Chapter 47's Go digest; cargo fmt --check exited 0
with no output while rustfmt --edition 2024 --check
fixtures/misformatted.rs exited 1 with the diff quoted above;
cargo clippy --offline --all-targets -- -D warnings exited 0
clean and --features lintbait failed with unneeded
`return` statement at src/lintbait.rs:13:5 naming
needless_return; and cargo test --offline reported
test result: ok. 1 passed; 0 failed. The drift transcript in the
cargo test section is also a real run, taken on a scratch copy
of the crate with the payload's last byte changed from 0x2e to
0x21. Not exercised: unverified — cargo-nextest,
cargo-deny, cargo-llvm-cov, and sccache
are absent from this reference host (confirmed by command -v for
each) and were not installed, and cargo-audit is present but
~/.cargo/advisory-db is absent, so gates E, F, G, and H each
printed an informational SKIP: rather than running; their
sections above show the committed configs (deny.toml,
clippy.toml) and the exact tokens (1 test run: 1
passed, bans ok, a digest.rs coverage row, a
RUSTSEC-YYYY-NNNN identifier) a reader with those tools installed
would see, rather than a captured run. The cargo audit advisory
block is explicitly labeled illustrative: this crate is zero-dependency by
design and no vulnerable dependency will be committed to it.
cargo-watch and cargo-flamegraph are installed on
this host but deliberately not gated (never terminates / needs
perf privileges and produces non-deterministic output), and
sccache's cache-hit rate is not a correctness property; miri is
cross-referenced to Chapters 29 and 43 and has no unsafe block
here to examine. examples/manifest.yaml marks this example
mode: local — no VM or LGTM path applies.