# How Rustlings Executes Exercises: Build, Test, Clippy, and Run Pipeline

> Discover how Rustlings executes exercises using its build, test, clippy, and run pipeline. Learn about the Rustlings command runner and exercise struct for a seamless learning experience.

- Repository: [The Rust Programming Language/rustlings](https://github.com/rust-lang/rustlings)
- Tags: internals
- Published: 2026-03-05

---

**Rustlings executes exercises through a sequential five-stage pipeline—build, test, clippy, run, and report—coordinated by the `Exercise` struct in [`src/exercise.rs`](https://github.com/rust-lang/rustlings/blob/main/src/exercise.rs) and the `CmdRunner` wrapper in [`src/cmd.rs`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/src/exercise.rs)**: Defines the `Exercise` struct and implements the `RunnableExercise` trait, which encapsulates the full build-test-clippy-run pipeline logic and state management.
- **[`src/cmd.rs`](https://github.com/rust-lang/rustlings/blob/main/src/cmd.rs)**: Provides the `CmdRunner` and `CargoSubcommand` types, thin wrappers around `std::process::Command` that handle Cargo target directory resolution via `cargo metadata` and manage output capture buffers.
- **[`src/run.rs`](https://github.com/rust-lang/rustlings/blob/main/src/run.rs)**: Contains the `run(app_state: &mut AppState)` orchestration function that coordinates a single exercise run, handles colored terminal output using `crossterm`, and manages application state transitions upon success or failure.
- **[`src/main.rs`](https://github.com/rust-lang/rustlings/blob/main/src/main.rs)**: The CLI entry point that parses subcommands—`run`, `check`, `test`, and `verify`—dispatching to the appropriate handlers in [`src/run.rs`](https://github.com/rust-lang/rustlings/blob/main/src/run.rs) or [`src/verify.rs`](https://github.com/rust-lang/rustlings/blob/main/src/verify.rs).

## Implementation Details from the Source

### Build Step Implementation

```rust
// 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`](https://github.com/rust-lang/rustlings/blob/main/exercise.rs) lines 14-17

### Test Execution Logic

```rust
// 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`](https://github.com/rust-lang/rustlings/blob/main/exercise.rs) lines 26-34

### Clippy Integration with Strict Mode

```rust
// 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`](https://github.com/rust-lang/rustlings/blob/main/exercise.rs) lines 44-51

### Binary Execution and Output Capture

```rust
// Execute the compiled binary
let run_success = run_bin(bin_name, output, cmd_runner)?;

```

*Source:* [`exercise.rs`](https://github.com/rust-lang/rustlings/blob/main/exercise.rs) lines 53-55

### Final Reporting Logic

```rust
// 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`](https://github.com/rust-lang/rustlings/blob/main/run.rs) lines 24-40

## Summary

- Rustlings executes exercises through a strict **build → test → clippy → run** pipeline defined in [`src/exercise.rs`](https://github.com/rust-lang/rustlings/blob/main/src/exercise.rs) and coordinated by [`src/run.rs`](https://github.com/rust-lang/rustlings/blob/main/src/run.rs).
- The `Exercise` struct tracks whether an exercise requires **unit testing** (`test` flag) or **strict Clippy enforcement** (`strict_clippy` flag).
- `CmdRunner` in [`src/cmd.rs`](https://github.com/rust-lang/rustlings/blob/main/src/cmd.rs) abstracts Cargo command construction and output capture, using `cargo metadata` to 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_CLIPPY` environment variable or per-exercise configuration, adding `-D warnings` to 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`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/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`](https://github.com/rust-lang/rustlings/blob/main/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.