Rust Tooling and Code Quality
Static Analysis
Static analysis inspects source code, Intermediate Representations (HIR and MIR), and dependency metadata at compile time without executing the program. Rust's type system, ownership and borrow checker, exhaustive pattern matcher, and ecosystem static analyzers form a multi-layered defense that catches entire classes of concurrency, memory, and API bugs before a binary ever runs.
Multi-Layered Static Analysis in Rust:
Layer 1: `rustc` Type & Borrow Checker (Mandatory)
- Ownership, lifetimes, `Send`/`Sync` data-race freedom, exhaustive `match`
Layer 2: `rustc` & `clippy` Lints (`[lints]` in Cargo.toml)
- `#[must_use]`, `unsafe_op_in_unsafe_fn`, 750+ Clippy static checks
Layer 3: Ecosystem Static Analyzers
- `cargo-semver-checks`, `cargo-udeps`, `cargo-geiger`, `cargo-llvm-lines`
Encoding Domain Invariants into the Type System
The strongest form of static analysis in Rust is making invalid states unrepresentable using the Newtype pattern and Type-State builders so illegal program transitions fail compilation rather than failing at runtime:
use std::marker::PhantomData;
// Type-state markers (Zero-Sized Types exist only at compile time!)
pub struct Unverified;
pub struct Verified;
pub struct SqlQuery<State> {
sql: String,
_state: PhantomData<State>,
}
impl SqlQuery<Unverified> {
pub fn new(raw_sql: impl Into<String>) -> Self {
Self {
sql: raw_sql.into(),
_state: PhantomData,
}
}
pub fn verify_read_only(self) -> Result<SqlQuery<Verified>, &'static str> {
let upper = self.sql.trim_start().to_ascii_uppercase();
if upper.starts_with("SELECT ") {
Ok(SqlQuery {
sql: self.sql,
_state: PhantomData,
})
} else {
Err("only SELECT queries are permitted on read-only replicas")
}
}
}
impl SqlQuery<Verified> {
// Only a `SqlQuery<Verified>` has an `.execute()` method!
// Calling `.execute()` on `SqlQuery<Unverified>` is a compile-time error.
pub fn execute(&self) -> String {
format!("Executed verified query: {}", self.sql)
}
}
fn main() {
let raw = SqlQuery::new("SELECT id, name FROM users");
let verified = raw.verify_read_only().unwrap();
println!("{}", verified.execute());
}
Executed verified query: SELECT id, name FROM users
Specialized Static Analysis Tools in the Rust Ecosystem
| Tool | Command | What It Analyzes Statically |
|---|---|---|
cargo-semver-checks |
cargo semver-checks |
Compares public API AST/rustdoc JSON against baseline releases to detect accidental SemVer breaking changes. |
cargo-udeps / cargo-machete |
cargo machete |
Scans workspace crates to detect unused dependencies declared in Cargo.toml. |
cargo-geiger |
cargo geiger |
Counts unsafe functions, expressions, traits, and impls across your crate and all transitive dependencies. |
cargo-llvm-lines |
cargo llvm-lines |
Measures which generic functions generate the most LLVM IR lines during monomorphization (helping reduce compile time and binary bloat). |
cargo-bloat |
cargo bloat --release |
Analyzes ELF/Mach-O/PE .text sections to show which functions and crates occupy the most bytes in the compiled executable. |