更严格的 Clippy 配置,帮你在生产环境少踩几个坑

Evan Schwartz 2026-08-12T12:28:07.572302

Rust 的编译器能挡掉不少错误,但很多会在生产环境里“爆雷”的问题,它其实也管不住。本文从一个真实的线上事故出发,聊聊为什么你应该把 Clippy 配置得比默认更严格——尤其在 AI 编码代理写代码越来越普遍的当下,一套更严格的静态检查规则,相当于给代码库加了一道重要的安全护栏。

一次生产事故:字符串切片引发的 panic

Scour 是我在做的个性化内容信息流服务。每周五,它都会给每个用户发送一封邮件摘要,汇总本周与他们兴趣最匹配的热门帖子。可就在某个周五,邮件发送任务突然停了下来。

这让我有点意外,因为此前我已经在类型系统层面做了不少防护,也写了测试,确保任务在任何错误发生时都能记下日志并继续跑。

但日志很快告诉了我真正的原因:线程 tokio-runtime-worker 发生了 panic——byte index 200 is not a char boundary(字节索引 200 不是字符边界)。也就是有个函数在截断文章摘要时,没有检查 UTF-8 的字符边界,直接按字节位切片,结果把正在处理邮件任务的 Tokio 工作线程给弄崩了。

这个问题本身修起来不难,换一种尊重 UTF-8 边界的截断方式就好。但它让我联想到 2025 年 Cloudflare 那个“搞崩互联网”的 unwrap bug——都是很基础的失误,却造成了相当大的影响。所以我想找一个更通用的方案,在问题发生之前就把它拦住。

编译通过 ≠ 万事大吉

Rust 编译器确实能挡掉很多 bug,但生产环境里仍然有一些问题是它抓不到的:

这些问题,编译器不一定能帮你发现。而把 Clippy 配置得比默认更严格,就能防住不少此类风险。

在编码代理流行的时代,这件事的意义更大了。经验丰富的 Rust 工程师或许能靠直觉避开一些容易出问题的写法,但编码代理或初级同事未必。更严格的 Clippy 规则,可以帮你更放心地依赖别人写的代码。而且,一次性地在现有代码库上启用新 lint,本身也是个繁琐的活儿——正好适合交给编码智能体来干。

启用更多 Clippy 检查

Clippy 自带几百个默认关闭的 lint。有些是因为容易误报才被关掉,有些则是风格上的选择,你可能合理地不想用。那我们该启用哪些检查,才能重新找回“只要编译通过,Clippy 也通过,就能放心运行”的感觉?

为什么不直接启用整个 lint 类别?

Clippy 的 lint 按类别分组,包括:

可惜的是,没有哪个类别能清晰地对应“别让它在生产环境里 panic 或出错”。Clippy 官方文档也明确提醒:Restriction 类别绝对不应该整体启用。Clippy 甚至专门提供了一个 lint(叫 blanket_clippy_restriction_lints),来阻止你一次性启用整个类别。

因为 Restriction 里既有许多有用的检查,也包含不少彼此矛盾的规则。比如,它同时包含强制使用大端字节序和强制使用小端字节序的 lint。文档的建议是:“启用之前应逐个考虑。”你当然可以先整体启用 pedanticrestriction,再单独放行某些不想要的项,但更推荐的做法是:逐个挑选,按需启用

不触发的 lint 也有价值

有些 lint 可能在你当前的代码库里完全不会触发,那是不是就不值得开?我不这么认为。

不触发的 lint 可以当作一条低成本的“绊线”。万一以后有人——不管是同事、你自己,还是某个编码智能体——引入了这种写法,它能立刻提醒你。相比让这类问题悄悄溜进生产环境,提前响一下警报要划算得多。

我的 lint 选择

在分享我的配置之前,先说两个前提:

下面这些 lint 是我常用的,按照它们防范的行为类型分了组。如果你只想直接抄配置,也可以跳到文末。

别崩溃(Don't Panic)

这一组 lint 主要负责防止 unwrap、不安全的切片或数组/字符串索引导致的 panic。

需要说明的是,其中一些 lint(比如 string_sliceindexing_slicing)可能会在你的整个代码库里产生大量警告,修起来相当繁琐。但换个角度看,改用 .get() 和迭代器这类更安全的写法,可以避开不少极其隐蔽的坑。我个人觉得,这笔账是算得过来的。

关键要点

本部分继续介绍更严格的 Clippy 配置:除了防范 panic 的 lint,还包括静默失败、异步死锁、内存安全、数字运算等几类问题。同时讨论了 expect_usedarithmetic_side_effects 这两个 lint 的实际取舍,帮助你在“安全”与“噪音”之间找到平衡。

继续:防范 panic 的 lint

以下这些检查专门拦截可能触发 panic 的代码写法:

至于 expect_used,要不要启用就看个人取舍了。调用 .expect 的确可能引发 panic,但传给它的消息本来就应该说明“为什么这里不会出问题”。如果启用了这个 lint,你就得在确实不会出错的地方写 #[expect(expect_used, reason = "...")] 逐个局部关闭,而这些 reason 往往和你原本写 .expect 时给出的理由完全一样,等于重复劳动。

另一个值得权衡的是 arithmetic_side_effects。它能拦住溢出和除零错误,代价是 Clippy 会对每一处用到 +-*<</% 等运算符的代码发出警告。作者在自己的代码库里试过开启它,估计约 15% 的警告抓到了真实问题,剩下的 85% 都是噪音。

不要静默失败

这些 lint 用于避免错误被悄悄吞掉:

不要做糟糕的异步操作

这些问题可能引发并发错误和死锁:

不要对内存做不安全的事情

不要用数字做可能出错的事情

至于 cast_possible_wrapcast_precision_losscast_possible_truncation 这几个 lint,它们会强制你在做可能有损的数字类型转换时,明确记录不变量。对有些人来说很有用,对另一些人可能觉得多余。

不要做容易避免的坏事

查看原文