Rust code formatting can be a real headache, especially on larger teams. You’ve got people with different editor settings, different ideas of what "readable" means, and suddenly your git diff is a mess of whitespace changes that completely obscure the actual code logic. cargo fmt is Rust’s built-in solution to this, and it’s designed to be a hammer that swings everyone into line with a single, opinionated style.
The most surprising thing about cargo fmt is how little configuration it actually needs to be effective. By default, it uses the standard Rustfmt style, which is what most of the Rust ecosystem uses. You don’t need to invent your own style guide; you just need to enforce the one that already exists.
Let’s see it in action. Imagine you have a Rust file, src/main.rs, that looks like this:
fn main() {
let x=5;
println!("Hello, world!");
}
Notice the extra space after let and before =. This is the kind of subtle inconsistency that cargo fmt will fix.
To run cargo fmt on your project, you simply navigate to your project’s root directory in your terminal and execute:
cargo fmt
After running this command, src/main.rs will be automatically reformatted to:
fn main() {
let x = 5;
println!("Hello, world!");
}
See? The extra space is gone. cargo fmt has applied the standard Rustfmt style. This is the core of its power: it’s not about choosing a style, it’s about adopting the default and making sure everyone adheres to it.
The mental model here is simple: cargo fmt is a tool that runs Rustfmt, which is a formatter designed by the Rust project itself. It takes your code, parses it, and rewrites it according to a predefined set of rules. These rules cover things like indentation, line breaks, spacing around operators, brace placement, and more. The goal is to eliminate subjective formatting decisions so developers can focus on the code’s logic.
You can integrate this into your development workflow in a few ways.
- Manual Execution: Developers can run
cargo fmtwhenever they want to format their code. This is the simplest approach but relies on developer discipline. - Editor Integration: Most modern Rust IDEs and editors (like VS Code with the rust-analyzer extension, or IntelliJ IDEA with the Rust plugin) can be configured to run
cargo fmtautomatically on save. This is highly effective because it formats the code before you even commit it. - Pre-commit Hooks: For a more robust enforcement, you can use pre-commit hooks. Tools like
pre-commit(a framework for managing multi-language pre-commit hooks) can be configured to runcargo fmton staged files. Ifcargo fmtmodifies any files, the commit will fail, forcing the developer to format their code before they can commit.
Here’s an example of how you might set up a .pre-commit-config.yaml file for pre-commit hooks:
repos:
- repo: https://github.com/rust-lang/rustfmt
rev: v1.6.0 # Use a specific, stable version
hooks:
- id: rustfmt
When you run git commit, this hook will automatically check your staged Rust files. If rustfmt finds anything to change, the commit will be aborted with a message indicating which files were not formatted. You’d then run cargo fmt manually to fix them, and git add them again before retrying the commit.
While cargo fmt enforces the standard Rustfmt style, it does have some limited configuration options. These are primarily controlled via a rustfmt.toml file in your project’s root directory. For example, if you wanted to slightly alter how imports are grouped, you could add:
# rustfmt.toml
imports_granularity = "Module"
This would tell Rustfmt to group imports at the module level rather than individual items. However, the advice is to stick to the defaults as much as possible. Overriding too many rules defeats the purpose of having a consistent, community-wide style. The strength of cargo fmt lies in its near-universal adoption of the default style.
One thing that often trips people up is how cargo fmt interacts with clippy. clippy is Rust’s linter, and it often suggests changes that might conflict with cargo fmt’s formatting. For example, clippy might suggest removing a blank line for conciseness, while cargo fmt might prefer that blank line for readability according to its rules. The general approach is to run cargo fmt first to ensure the code is properly formatted, and then run clippy to catch logical or stylistic issues. Many editor integrations can be configured to run cargo fmt on save and then clippy on a separate trigger or command.
The next step after achieving consistent formatting is ensuring your code is also free of common logical and performance pitfalls, which is where cargo clippy comes in.