How Rustlings Executes Exercises: Build, Test, Clippy, and Run Pipeline
Rustlings executes exercises through a sequential five-stage pipeline—build, test, clippy, run, and report—coordinated by the Exercise struct in src/exercise.rs and the CmdRunner wrapper in src/cmd.rs, with strict Clippy linting optional via the strict_clippy flag or FORCE_STRICT_CLIPPY environment variable.
Rustlings is the official interactive tutorial for learning Rust, maintained in the rust-lang/rustlings repository. When you invoke rustlings run exercise_name, the tool doesn't simply compile your code—it orchestrates a comprehensive validation workflow to ensure correctness and idiomatic style. Understanding how Rustlings executes exercises helps contributors debug the toolchain and learners interpret the feedback loop.
The Five-Stage Execution Pipeline
1. Locating the Current Exercise
The pipeline begins in src/run.rs where the run(app_state: &mut AppState) function retrieves the target exercise via AppState::current_exercise(). This returns an Exercise struct containing critical metadata: whether the exercise requires tests (exercise.test) and whether it demands strict Clippy checking (exercise.strict_clippy).
2. Building the Binary
Inside src/exercise.rs, the run_exercise method calls RunnableExercise::run::<false>, which instantiates a CmdRunner to execute Cargo build. The CmdRunner::cargo("build", bin_name, ...) method constructs a Cargo command pointing at the development Cargo.toml and the target directory discovered via cargo metadata. If compilation fails, the pipeline aborts immediately before proceeding to tests.
3. Running Unit Tests
If exercise.test returns true, Rustlings invokes Cargo test through the same CmdRunner. The command includes --color always and --format pretty flags to preserve test output formatting. Notably, if tests fail, the binary still executes once via run_bin to show runtime output before the pipeline terminates with an error status. This logic appears in exercise.rs lines 26-34.
4. Linting with Clippy
After successful compilation and testing, Cargo clippy runs with the --profile test flag to ensure test code is also linted. If the FORCE_STRICT_CLIPPY environment variable is set or exercise.strict_clippy() returns true, the command appends -D warnings to treat all warnings as errors. This strict mode enforcement is handled in exercise.rs lines 44-51.
5. Executing the Binary and Reporting
Finally, run_bin executes the compiled binary via CmdRunner::run_debug_bin, merging stdout and stderr into a shared output buffer. src/run.rs collects success flags from each stage—if any step failed, it prints a red error banner; otherwise, it displays a green "✓ Successfully ran" message and updates the exercise progress state in AppState.
Core Types and Source Files
The execution pipeline relies on four primary source files:
src/exercise.rs: Defines theExercisestruct and implements theRunnableExercisetrait, which encapsulates the full build-test-clippy-run pipeline logic and state management.src/cmd.rs: Provides theCmdRunnerandCargoSubcommandtypes, thin wrappers aroundstd::process::Commandthat handle Cargo target directory resolution viacargo metadataand manage output capture buffers.src/run.rs: Contains therun(app_state: &mut AppState)orchestration function that coordinates a single exercise run, handles colored terminal output usingcrossterm, and manages application state transitions upon success or failure.src/main.rs: The CLI entry point that parses subcommands—run,check,test, andverify—dispatching to the appropriate handlers insrc/run.rsorsrc/verify.rs.
Implementation Details from the Source
Build Step Implementation
// Build the exercise binary
let build_success = cmd_runner
.cargo("build", bin_name, output.as_deref_mut())
.run("cargo build …")?;
if !build_success { return Ok(false); }
Source: exercise.rs lines 14-17
Test Execution Logic
// Run the test (if any)
if self.test() {
let mut test_cmd = cmd_runner.cargo("test", bin_name, output.as_deref_mut());
test_cmd.args(["--", "--color", "always", "--format", "pretty"]);
let test_success = test_cmd.run("cargo test …")?;
if !test_success {
run_bin(bin_name, output, cmd_runner)?;
return Ok(false);
}
}
Source: exercise.rs lines 26-34
Clippy Integration with Strict Mode
// Run Clippy (strict or normal)
let mut clippy_cmd = cmd_runner.cargo("clippy", bin_name, output.as_deref_mut());
if FORCE_STRICT_CLIPPY || self.strict_clippy() {
clippy_cmd.args(["--profile", "test", "--", "-D", "warnings"]);
} else {
clippy_cmd.args(["--profile", "test"]);
}
let clippy_success = clippy_cmd.run("cargo clippy …")?;
Source: exercise.rs lines 44-51
Binary Execution and Output Capture
// Execute the compiled binary
let run_success = run_bin(bin_name, output, cmd_runner)?;
Source: exercise.rs lines 53-55
Final Reporting Logic
// Final reporting (run.rs)
if !success {
stdout.write_all(b"Ran ")?;
app_state.current_exercise()
.terminal_file_link(&mut stdout, app_state.emit_file_links())?;
stdout.write_all(b" with errors\n")?;
return Ok(ExitCode::FAILURE);
}
stdout.queue(SetForegroundColor(Color::Green))?;
stdout.write_all("✓ Successfully ran ".as_bytes())?;
stdout.write_all(exercise.path.as_bytes())?;
Source: run.rs lines 24-40
Summary
- Rustlings executes exercises through a strict build → test → clippy → run pipeline defined in
src/exercise.rsand coordinated bysrc/run.rs. - The
Exercisestruct tracks whether an exercise requires unit testing (testflag) or strict Clippy enforcement (strict_clippyflag). CmdRunnerinsrc/cmd.rsabstracts Cargo command construction and output capture, usingcargo metadatato dynamically locate the target directory.- Test failures trigger graceful degradation: the binary executes once to display output before the pipeline exits with an error code.
- Strict Clippy mode activates via the
FORCE_STRICT_CLIPPYenvironment variable or per-exercise configuration, adding-D warningsto fail on any lint warning.
Frequently Asked Questions
What happens if a Rustlings exercise fails to compile?
If cargo build returns a non-zero exit code in src/exercise.rs, the build_success boolean evaluates to false and the pipeline aborts immediately. No tests, Clippy checks, or binary execution occur. The user sees a red error banner in the terminal indicating the compilation failed, along with the captured compiler output.
Does Rustlings run Clippy on test code?
Yes. The Clippy command explicitly includes the --profile test argument to ensure that code inside #[cfg(test)] blocks and test functions is linted alongside the main binary. This is implemented in exercise.rs lines 44-51, ensuring learners receive style feedback on their test implementations as well as their application logic.
How does Rustlings handle test failures?
When cargo test fails (lines 26-34 of exercise.rs), the tool executes run_bin once to display the program's runtime output to the user, then returns Ok(false) to signal failure. This design ensures learners see both the test framework's output and any debugging information from the actual binary execution before the pipeline terminates.
Can I force strict Clippy checking for all exercises?
Yes. Set the FORCE_STRICT_CLIPPY environment variable before running rustlings. When this variable is present, every Clippy invocation appends -D warnings to the command arguments, treating all lint warnings as hard errors regardless of the individual exercise's strict_clippy setting in the exercise metadata.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →