Skip to content
rusthacks.dev
Tooling

Rustfmt Configuration and Formatting Checks in CI Explained

Format Rust code the same way everywhere with rustfmt: the few settings worth changing, the nightly-only options to avoid, and a CI check that fails on unformatted code.

2 min read
Summarize in AI
Flat illustration for the post “Rustfmt Configuration and Formatting Checks in CI Explained”, composed around deploy.

Formatting arguments are the cheapest ones to prevent. rustfmt has one canonical output, nearly everyone in the Rust community uses it, and a CI check keeps the code in that shape. The configuration file is mostly a place to resist the urge to tweak. This post covers the command, the few stable options and the CI line. It belongs with Clippy and lints in Cargo.toml.

Rust 1.90, rustfmt bundled.

Commands

Shell
cargo fmt                      # format the package
cargo fmt --all                # every workspace member
cargo fmt --all -- --check     # exit code 1 and a diff if anything changes

A formatting failure in CI prints the diff:

Text
Diff in src/main.rs:3:
-fn main(){println!("hi")}
+fn main() {
+    println!("hi")
+}

The fix is to run cargo fmt locally and commit. Pin the toolchain with rust-toolchain.toml, because rustfmt output can differ slightly between releases.

The file

rustfmt.tomlTOML
max_width = 100
use_small_heuristics = "Max"

Both are stable options. max_width defaults to 100 and use_small_heuristics = "Max" lets short structs and calls fit on one line. Edition and style edition come from Cargo.toml; the 2024 edition ships a new style edition with small formatting changes, such as how version sorting treats names in use lists.

Options to leave alone

Tempting settings like imports_granularity, group_imports and wrap_comments are unstable and work only with cargo +nightly fmt. If half the team formats with stable and half with nightly, every pull request flips lines. Either run nightly rustfmt for everyone, through a pinned nightly in CI, or skip these options. For import order, accept the default and let rust-analyzer's "organize imports" do the grouping by hand.

Skipping

rust
#[rustfmt::skip]
const TABLE: [[u8; 3]; 3] = [
    [1, 0, 0],
    [0, 1, 0],
    [0, 0, 1],
];

The attribute leaves that item alone. Use it for tables and bit patterns where alignment carries meaning. Skipping a whole function because the formatter makes it ugly is a sign the function should be smaller.

In an editor

With rust-analyzer, format on save calls rustfmt on the file. Check rust-analyzer setup for the setting. Pre-commit hooks that run cargo fmt --check catch problems before CI, which matters when CI queues are slow.

Generated code

Add paths to ignore in rustfmt.toml (an unstable option) or keep generated files out of the workspace's source tree and include them with include! from OUT_DIR, which rustfmt does not touch. Build scripts writing to OUT_DIR produce exactly this layout.

Limits

Rustfmt does not reflow comments or macro bodies it cannot parse. Macro invocations with unusual syntax are left as written. The options are listed on the rustfmt configuration page, with each marked stable or unstable.

Taggedrustfmt
Share

Keep reading

Flat illustration for the post “Incident Response for Rust Services a Short Runbook”, composed around deploy.
DevOps

Incident Response for Rust Services a Short Runbook

A one-page runbook for when a Rust service misbehaves: stabilize first, check recent changes, read the four golden signals, find the failing dependency, and use the Rust-specific evidence: panics, memory, executor stalls.

4 min read
Flat illustration for the post “Debugging Async Backtraces and Task Dumps in Tokio”, composed around async.
Tooling

Debugging Async Backtraces and Task Dumps in Tokio

A hung async service has no useful thread backtrace. Tokio task dumps capture the await stack of every task. Enable tokio_unstable and taskdump, trigger a dump from a signal or endpoint, and read where each task is parked.

4 min read

Get new posts by email

We send one email when a new post is published. No spam.

Unsubscribe in one click. No spam, ever.