AI 辅助编程的边界探索:7 月实验告诉我们 AI 能做什么不能做什么的结论
一、7 月实验设计:有边界地测试,而不是盲目依赖
实验设定很简单:7 月的每一次编程任务,先自己尝试,遇到卡点再用 AI,然后记录 AI 给出的答案是否可用、是否需要修改、修改了多久、最终效果如何。31 天里我记录了 127 次 AI 交互,按场景分成了四类。
最让我意外的是各个场景的成功率差异巨大:
代码解释 72% 的一次性正确率很高——AI 在"翻译"已有代码时表现优秀。代码生成 45% 的正确率——差不多一半能直接用。Bug 排查 31%——经常指错方向。架构设计只有 18%——几乎每次都要大规模调整。
二、AI 能做什么:三个经过验证的高效场景
经过 31 天的密集实验,我确认了 AI 在三个场景下确实能大幅提升效率:
场景一:代码翻译和解释。 这是 AI 最强的领域。给我一段 Rust async 代码解释各部分的职责、给我解释这段 Cargo.toml 里的 features 配置是什么意思、把这段 Python 脚本翻译成 Rust——这些任务 AI 完成得又快又准。
// ============================================================
// 场景一示例:AI 帮我解释这段所有权代码
// 我写了这段,但希望确认自己的理解是否正确
// ============================================================
/// 统计一个字符串中某个子串的出现次数
///
/// 设计选择:使用 &str 而非 String,因为函数不需要拥有数据
/// 只需要临时读取——遵循"最小权限原则"
fn count_occurrences(text: &str, pattern: &str) -> usize {
// text.matches 返回一个迭代器,不会分配新内存
// count 消费迭代器,统计匹配次数
text.matches(pattern).count()
}
fn main() {
let s = "hello rust hello world hello rust";
// AI 帮我确认:这里传 &str 是因为字符串字面量本身就是 &str
// 如果是 String 变量,需要 &s 来传递引用
assert_eq!(count_occurrences(s, "hello"), 3);
assert_eq!(count_occurrences(s, "rust"), 2);
}
场景二:代码生成(有模板的)。 AI 在生成样板代码时特别高效。比如"给我写一个用 clap 解析命令行参数的基础模板"、"用 serde 定义一个包含这些字段的结构体"、"写一个 reqwest 的 POST 请求骨架"。这些有明确模式的东西,AI 几乎每次都产出能用的代码。
场景三:错误信息解读。 Rust 编译器的错误信息已经非常详细了,但 AI 能把它们翻译成"人话"。比如 borrow checker 报的 cannot borrow *self as mutable because it is also borrowed as immutable,AI 能逐行分析哪里发生了不可变借用、哪里发生了可变借用、它们的生命周期关系是什么。
// ============================================================
// 场景三示例:一段真实报错,AI 帮我分析根本原因
// 编译错误:cannot borrow self.data as mutable more than once at a time
// ============================================================
struct Processor {
data: Vec<String>,
}
impl Processor {
// ❌ 错误写法 — 同时对 self.data 进行了两次可变引用
// fn bad_process(&mut self) {
// let a = &mut self.data; // 第一次可变借用
// let b = &mut self.data; // 第二次可变借用,违反了"同一时间只有一个可变借用"
// a.push("hello".to_string());
// }
// ✅ 正确写法 — 把两次可变借用拆开,分作用域
fn good_process(&mut self) {
{
let a = &mut self.data; // 第一个可变借用,在块内
a.push("hello".to_string());
} // a 的生命周期在这里结束,释放借用
// 此时 self.data 不再被借用,可以再次获取可变引用
let b = &mut self.data; // 第二个可变借用,合法
b.push("world".to_string());
}
}
三、AI 不能做什么:四个已经被证实的盲区
反面教训比正面经验更值钱。以下四个场景,我验证了 AI 真的不行——或者至少不能依赖:
盲区一:零上下文的大型重构。 我把一段 800 行的 main.rs 贴给 AI,让它"帮我拆成 5 个模块",出来的结果惨不忍睹。模块职责混乱、循环依赖、甚至引用了不存在的类型。AI 缺乏对整个项目上下文的理解,拆模块这种全局决策必须人工设计。
盲区二:异步代码的正确性。 Tokio 的并发模型、spawn 和 block_on 的区别、哪些函数需要 Send + Sync——这些是 AI 频繁出错的领域。它经常生成"看起来像 async 代码"的东西,但实际运行会 deadlock 或 panic。异步编程的正确性必须靠人工理解和测试验证。
// ============================================================
// AI 容易写错的异步代码示例
// ============================================================
use tokio::sync::Mutex;
use std::sync::Arc;
/// 这是一个 AI 容易出错但看起来很合理的模式
/// 实际上如果持有锁的跨越 .await,可能导致死锁
struct SharedState {
// 注意:用 tokio::sync::Mutex 而不是 std::sync::Mutex
// tokio 的 Mutex 能在 .await 时安全释放锁
data: Arc<Mutex<Vec<String>>>,
}
impl SharedState {
/// 原子地检查并插入数据
/// 必须确保 check 和 insert 在同一个锁保护区域内完成
async fn check_and_insert(&self, item: String) -> bool {
let mut data = self.data.lock().await;
if data.contains(&item) {
false // 已存在,不插入
} else {
data.push(item);
true // 插入成功
}
// MutexGuard 在这里自动释放
}
}
盲区三:性能优化建议。 AI 经常建议"用 HashMap 替代 Vec"或"加个索引",但这些建议脱离实际数据量和访问模式。我问 AI 为什么慢,它说"可能是 I/O 阻塞",但实际是我的 Arc 拷贝太多。性能问题必须靠 profiling,不是靠 AI 猜。
盲区四:架构设计决策。 这是正确率最低的领域(18%)。"我应该用 trait 还是 enum 做多态" "插件系统用宏注册还是手动注册" "场景管理和任务调度怎么分层"——这些问题 AI 的回答往往"不全错",但缺失关键约束和权衡。架构需要的是对项目全局的理解和取舍——这是人的领域。
四、AI 辅助编程的正确打法:一个三层模型
31 天下来,我对 AI 的使用策略收敛到了一个三层模型:
第一层(必须自己写):架构设计、性能关键路径、安全敏感代码。这些东西错了会引发连锁问题,而且 AI 缺乏足够的上下文做决策。
第二层(AI 辅助但必须验证):代码翻译、测试生成、错误解释。AI 在这些场景下能大幅提速,但必须逐行阅读和验证——它可能看起来都对,但细节有坑。
第三层(放心用 AI):样板代码、序列化结构体、CLI 参数定义、Cargo.toml 配置。这些有明确定型和重复模式的东西,AI 几乎不会出错。
五、总结
7 月的实验结论用一个比喻:AI 是一辆导航仪,不是自动驾驶。它能帮我找路、提醒我障碍、省掉走错路的时间——但方向盘始终要我自己握。用 AI 学 Rust,最危险的不是它犯错,而是我自己分辨不出来它什么时候在犯错。
三条实验结论:
- AI 在解释已有代码上最可靠(72%),在生成新逻辑上中等(45%),在架构决策上不可靠(18%)——用对场景很重要。
- 异步代码和性能优化是 AI 的重灾区——这些必须靠人工验证,不能盲信。
- 最好的姿势是三层模型:简单样板放心交给 AI,中等难度 AI 辅助但自己验证,核心逻辑必须自己写。
8 月我会继续在 AI 辅助上做实验,但方向会从"让 AI 帮我写代码"转向"让 AI 帮我 review 代码"——这个角色翻转可能会更有价值。

16万+

被折叠的 条评论
为什么被折叠?



