Map
โ†‘Clippy

Your Clippy config should be stricter

Wiki summaryrustclippylintingcoding-agents โ†ณ show in map Markdown
title
Your Clippy config should be stricter
type
summary
summary
Selective Clippy lints to recover the "if it compiles, it works" feeling โ€” especially relevant when coding agents are writing the patches
parent
clippy
tags
rust, clippy, linting, coding-agents
created
2026-04-30
updated
2026-07-22

Evan Schwartz, who runs the personalized-feed product Scour, lost his Friday email-digest job to a Tokio worker panic: byte index 200 is not a char boundary. A function had truncated article summaries by byte index without respecting UTF-8 character boundaries. The compiler couldn't catch it. The fix was a UTF-8-safe truncation, but the deeper question was: which Clippy lints would have caught this, and what other panicky patterns are waiting to fire?

The motivating concern is broader than one bug. He invokes the November 2025 Cloudflare unwrap outage as the high-profile reminder that Rust's "if it compiles, it works" maxim has gaps: panics, dropped futures, deadlocks, silent numeric mistakes. Stricter Clippy lints close some of those gaps. They matter even more when a coding agent is producing the code โ€” a seasoned engineer might avoid these patterns by reflex, but an agent or junior contributor won't, and enabling new lints on a legacy codebase is exactly the kind of grunt work an agent is good at.

Why not just enable a category

Clippy ships hundreds of lints in categories: Correctness, Suspicious, Complexity, Perf, Style, Pedantic, Restriction, Cargo, Nursery, Deprecated. None of them maps cleanly to "don't let this panic in production." The restriction category in particular is documented as something you should not enable wholesale โ€” it includes mutually-contradictory lints (e.g., big_endian_bytes and little_endian_bytes). The post argues for opting in lint-by-lint.

He also flags a useful framing: a lint that doesn't fire today is still useful, because it's a tripwire for the day someone (you, a colleague, an agent) writes the pattern.

The lints, by intent

Don't panic. Catch the unwrap/slice/index footguns. string_slice would have caught the original bug. Also: indexing_slicing, unwrap_used, panic, todo, unimplemented, unreachable, get_unwrap, unwrap_in_result, unchecked_time_subtraction, panic_in_result_fn. He notes expect_used is borderline (the .expect() message often documents the invariant well enough), and arithmetic_side_effects produced ~85% noise to ~15% real issues in his codebase.

Don't fail silently. let_underscore_future (dropped futures), let_underscore_must_use (swallowed Result), unused_result_ok (.ok() discarding errors), map_err_ignore (.map_err(|_| MyErr) losing source), assertions_on_result_states (asserting is_ok without surfacing the error message).

Don't do bad async stuff. await_holding_lock, await_holding_refcell_ref, if_let_mutex (only pre-2024 edition), large_futures (oversized futures cause stack overflow).

Don't do unsafe things with memory. mem_forget, undocumented_unsafe_blocks (every unsafe {} needs a // SAFETY: comment), multiple_unsafe_ops_per_block (one op per block so each gets its own justification), unnecessary_safety_doc / unnecessary_safety_comment.

Don't do potentially incorrect things with numbers. float_cmp, float_cmp_const, lossy_float_literal, cast_sign_loss, invalid_upcast_comparisons. Optional and stricter: cast_possible_wrap, cast_precision_loss, cast_possible_truncation โ€” together they force you to document numeric invariants at every lossy cast.

Easy wins. rc_mutex (Rc<Mutex<_>> is wrong), debug_assert_with_mut_call (debug/release behavior divergence), iter_not_returning_iterator, expl_impl_clone_on_copy, infallible_try_from, dbg_macro.

Don't allow your way around these lints. allow_attributes (forces #[expect(..., reason = "โ€ฆ")] over silent #[allow]) and allow_attributes_without_reason. These two are the agent-specific killer combo: an agent can't silently disable a lint to make its own code pass; it has to write down why.

Workspace gotcha

Cargo workspace lints don't auto-inherit. Each member crate has to opt in with lints.workspace = true. The post recommends cargo-workspace-lints (or a CI shell script) to enforce it; nightly has missing_lints_inheritance for the same purpose.

Warn vs deny

He prefers warn = "..." plus cargo clippy -- -D warnings on CI, so local iteration isn't blocked but the bar at commit time is the same.

Test relaxations

A clippy.toml at the workspace root relaxes the strict lints in tests, where unwrap/panic/indexing are normal:

allow-indexing-slicing-in-tests = true
allow-panic-in-tests = true
allow-unwrap-in-tests = true
allow-expect-in-tests = true
allow-dbg-in-tests = true

Why this is more than a config dump

The agent angle is the part that generalizes. The post is implicitly about clean-code-coding-agents from a different direction: code structure isn't only about humans reading less. It's about narrowing the surface of bad-pattern templates an agent can match on. A lint that prevents an agent from generating &s[..200] is doing the same work as a tightly-scoped function signature โ€” both are constraints that make the agent's output more likely to be right.