# Your Clippy config should be stricter

[[emschwartz-blog|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:

```toml
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.
