行业资讯
📅 2026/7/8 14:49:27
用 AI 给 Rust 项目做代码评审:先跑 clippy,再让模型提建议
用 AI 给 Rust 项目做代码评审先跑 clippy再让模型提建议一、自己写的代码真的很难看出毛病我有个习惯——写完一段代码后放一两个小时再回来看。这个方法有时候管用但更多时候是看来看去都觉得写得挺好。人的认知盲区很难靠自己突破。你写代码时的思维定势回看时依然在。如果有同事帮忙 review 当然最好但很多时候是 solo 开发或者时间上凑不到一起。后来我开始试一个方法把代码交给 AI 模型 review。但不是直接把代码扔给模型那么简单——我发现先用 Rust 自己的clippy跑一遍把静态分析的结果也附上模型给出的建议会具体得多少了很多泛泛而谈的东西。这篇文章就聊聊这个组合拳的实操方法。二、两阶段代码评审流程把 clippy 和 AI 模型组合起来评审流程是这样的flowchart TD A[提交代码] -- B[运行 cargo clippy] B -- C{clippy 有警告?} C --|有| D[记录 clippy 警告信息] C --|无| E[标记: 静态检查通过] D -- F[组织评审上下文] E -- F F -- G[将代码 clippy 结果 评审要求发给 AI 模型] G -- H[AI 模型分析并提出建议] H -- I[解析 AI 返回的建议] I -- J[人工逐条审核] J -- K{建议是否采纳?} K --|采纳| L[修改代码] K --|拒绝| M[记录原因] L -- N[再次运行 clippy 验证] M -- N N -- O[评审完成]这个流程里 clippy 负责规则性的硬问题——比如不必要的 clone、可以简化的模式匹配、可能的溢出。AI 模型负责需要上下文的软问题——比如命名是否清晰、逻辑是否合理、是否有更好的设计模式。三、Prompt 模板与实战3.1 评审用的 Prompt 模板我整理了一个效果比较稳定的 Prompt你是一位资深的 Rust 代码审查员。请从以下几个方面评审以下代码 1. **正确性**逻辑是否有 bug边界条件是否处理到位 2. **安全性**是否有潜在的 panic、unsafe 使用不当、数据竞争 3. **性能**是否有不必要的分配、克隆、或低效的数据结构 4. **可读性**命名是否清晰是否需要增加注释说明 5. **Rust 惯用法**是否可以用更 idiomatic 的写法替代 以下是 clippy 的静态分析结果 --- {clippy_output} --- 以下是需要评审的代码 rust {code}请逐条给出建议每条建议标注严重程度 必须修复 / 建议改进 / 可选优化涉及的行数范围具体修改建议如果需要给出修改后的代码片段不要给出与 clippy 结果重复的建议。### 3.2 实战示例评审一段有问题的代码 假设我们有这样一段待评审的代码处理用户配置的读取和缓存 rust use std::collections::HashMap; use std::fs; /// 配置管理器负责读取和管理用户配置 struct ConfigManager { configs: HashMapString, String, loaded: bool, } impl ConfigManager { fn new() - Self { ConfigManager { configs: HashMap::new(), loaded: false, } } fn load_config(mut self, path: str) { let content fs::read_to_string(path).unwrap(); for line in content.lines() { let parts: Vecstr line.split().collect(); if parts.len() 2 { self.configs.insert( parts[0].to_string(), parts[1].to_string(), ); } } self.loaded true; } fn get_value(self, key: str) - String { if !self.loaded { panic!(配置尚未加载); } self.configs.get(key).unwrap().clone() } fn has_key(self, key: str) - bool { self.configs.contains_key(key) } } fn main() { let mut manager ConfigManager::new(); manager.load_config(app.conf); println!({}, manager.get_value(username)); }先运行 clippy$ cargo clippy warning: called unwrap() on a Result value -- src/main.rs:15:45 | 15 | let content fs::read_to_string(path).unwrap(); | ^^^^^^ help: handle the error: ? warning: called unwrap() on an Option value -- src/main.rs:32:43 | 32 | self.configs.get(key).unwrap().clone() | ^^^^^^ help: use ?: self.configs.get(key)? warning: field loaded is never read directly -- src/main.rs:6:5 | 6 | loaded: bool, | ^^^^^^^^^^^^然后把代码和 clippy 结果一起发给 AI 模型模型可能会返回类似这样的建议 必须修复正确性 - 第15行fs::read_to_string 可能失败文件不存在、权限不足等 使用 unwrap() 会导致 panic。建议将 load_config 的返回类型改为 Result。 - 第32行配置中不存在的 key 不应该 panic。建议返回 OptionString。 建议改进Rust 惯用法 - 第3行HashMapString, String 作为配置存储不够灵活。 建议封装一个 ConfigValue 枚举来支持不同类型的配置值。 - 第19-23行split().collect() 在 lines 循环中可能产生大量临时分配。 建议使用 split_once() 来避免 Vec 分配和 lines() 迭代中的额外开销。 可选优化可读性 - loaded 字段的逻辑可以整合到 configs 的状态中 如果 configs 为空且来自文件则为未加载不需要单独的布尔标记。3.3 评审意见的筛选与合并AI 的建议不能盲从需要人工过一遍。我通常按这个优先级处理// 修改后的 ConfigManager融合了 clippy 和 AI 的建议 use std::collections::HashMap; use std::fs; /// 配置管理器 /// 使用 Result 类型传递错误避免 panic struct ConfigManager { configs: HashMapString, String, } #[derive(Debug)] enum ConfigError { /// 配置文件读取失败 IoError(std::io::Error), /// 配置格式错误某行缺少 分隔符 ParseError(String), } impl Fromstd::io::Error for ConfigError { fn from(e: std::io::Error) - Self { ConfigError::IoError(e) } } impl ConfigManager { fn new() - Self { ConfigManager { configs: HashMap::new(), } } /// 从文件加载配置返回 Result 而不是 panic fn load_config(mut self, path: str) - Result(), ConfigError { let content fs::read_to_string(path)?; // ✅ 用 ? 传播错误 for line in content.lines() { // ✅ 跳过空行和注释行 let trimmed line.trim(); if trimmed.is_empty() || trimmed.starts_with(#) { continue; } // ✅ 使用 split_once 避免 Vec 分配 if let Some((key, value)) trimmed.split_once() { self.configs.insert( key.trim().to_string(), value.trim().to_string(), ); } else { return Err(ConfigError::ParseError( format!(无效的配置行: {}, trimmed) )); } } Ok(()) } /// 获取配置值返回 Option不 panic fn get_value(self, key: str) - Optionstr { self.configs.get(key).map(|s| s.as_str()) } }可以看到修改后的代码用?操作符替代了unwrap()错误传播更安全用split_once替代了split collect减少了不必要的内存分配get_value返回Optionstr而不是 panic增加了对空行和注释行的处理去掉了冗余的loaded字段增加了ConfigError自定义错误类型四、边界与注意事项AI 可能给出错误建议。模型不了解你的完整业务上下文有时会建议把本来正确的逻辑改错。比如它可能建议把某个unwrap()换成?但你看代码上下文后发现那个unwrap()前面有逻辑保证了它不会失败。安全相关的修改必须人工复审。涉及 unsafe 代码、加密算法、权限检查的修改建议AI 的分析可能不够深入。这类代码的评审还是要以自己的判断为准。不要过度依赖。AI 评审是一个辅助手段不能替代理解自己代码的过程。我一般是写完一段逻辑比较复杂的代码后用它扫一遍看看有没有自己遗漏的改进点。Prompt 的针对性很重要。如果代码涉及特定领域比如嵌入式、GPU 计算需要在 Prompt 中补充领域上下文否则模型可能给出不切实际的建议。五、总结clippy AI的组合思路本质上是分工clippy 负责硬规则编译器和静态分析工具的强项AI 模型负责软判断需要经验和语感的弱项。两者加在一起覆盖的面比单独使用任何一种都要广。实际用下来这套流程帮我发现了不少自己写代码时的盲区——特别是一些虽然能跑但不够 idiomatic的写法。clippy 虽然也能覆盖一部分但 AI 给出的解释更详细修改建议也更贴合上下文。如果你也是 solo 开发的场景不妨试试这个组合。不用每次都跑复杂逻辑或者重构的时候跑一遍就行。成本很低一次 API 调用几毛钱但能帮到你发现的改进点可能价值远不止这些。