AI 辅助编程的 7 个误区:把模型当高级搜索引擎是对它的最大浪费
一、当 AI 说"这里加一个 Mutex 就好"的时候
去年十二月,我在给 dayuan 做并发缓存层。ChatGPT 很自信地建议:"在 HashMap 外层包一个 Arc<Mutex<>>,多线程访问就安全了。"
我照做了。然后发现高并发场景下吞吐量从 5000 QPS 跌到了 300 QPS——因为所有读操作都在争同一个锁。最后我用 dashmap + 分片锁的方案才把性能拉回来。
这事让我明白一个道理:AI 给你的方案永远只是"能编译通过"的方案,不是"正确"的方案,更不是"好"的方案。
作为自学编程的人,我天然依赖 AI 来弥补知识短板。但一年多下来,我发现很多人在用 AI 编程时,陷入了 7 个典型的认知误区。
二、误区全景
三、从"使用工具"到"被工具使用"
误区 1:把 AI 当高级搜索引擎
这是最常见的用法——也是最浪费的用法。
用户:tokio::spawn 怎么用?
AI: 这是文档……
你在浪费模型的推理能力。你不是在用一个代码助手,你是在用一个美化版的 grep docs.rs。
正确的用法:
用户:我在写一个 WebSocket 服务,需要在 accept 新连接时
同时维护一个全局的在线用户列表,支持广播。
现在我用 Arc<Mutex<Vec<u64>>> 存储在线用户 ID。
每次广播都要遍历一遍,5000 并发时 Mutex 变成瓶颈了。
不用给我写全代码,帮我分析三种可能的优化方向,
以及每种在什么场景下适合。
AI: 好,我来分析:
1. dashmap + 连接级别锁:
适用:读写比例均衡,需要强一致性
代价:内存占用比 Vec 高 30%
2. 分片路由 + 无锁广播:
适用:写少读多(在线状态变更少,广播多)
代价:实现复杂度高,需要设计分片策略
3. 事件总线 + 订阅模式:
适用:广播是主要操作,不需要存储状态快照
代价:需要引入消息中间件(如 tokio::broadcast)
看到了吗?你不是在问"怎么用",你是在把自己的设计困境描述给 AI,让它帮你做 trade-off 分析。
误区 2:无脑接受 AI 的代码
/// ❌ AI 写的代码,看起来没问题,直到你在生产环境看到 OOM
async fn process_all_users(user_ids: Vec<u64>) -> Vec<UserInfo> {
let mut tasks = Vec::new();
for id in user_ids {
// AI 不知道你的 user_ids 可能有 10 万个!
tasks.push(tokio::spawn(async move {
fetch_user_info(id).await
}));
}
// 10 万个 task 同时运行 → 连接池耗尽 → OOM
let mut results = Vec::new();
for task in tasks {
results.push(task.await.unwrap());
}
results
}
每次收到 AI 代码,带着这五个问题审一遍:
- 边界条件:如果输入为空会怎样?如果输入有 10 万条会怎样?
- 错误处理:每个
.await点都可能出错,处理了吗? - 性能假设:这个操作是 O(1) 还是 O(n)?瓶颈在哪里?
- 并发安全:多线程/多协程同时运行时,有数据竞争吗?
- 资源泄露:文件句柄、网络连接、内存有没有正确释放?
误区 3:跳过理解,直接复制粘贴
这是我刚开始用 ChatGPT 时最大的毛病。看到代码跑通了就以为学会了,一周后再遇到同样的问题——还是不会。
我的实践:AI 辅助学习的正确姿势
/// 场景:AI 生成了这段代码帮你解决问题
/// 但你没有直接粘贴,而是逐行加注释来确保自己理解了
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
/// 线程安全的缓存实现
/// 追问 AI 的问题:
/// Q: 为什么用 RwLock 而不是 Mutex?
/// A: 因为缓存的读操作远多于写操作,RwLock 允许多个读并发
/// Q: 为什么外面要包 Arc?
/// A: 因为 RwLock 需要被多个 task 共享,Arc 提供多所有权
pub struct Cache {
inner: Arc<RwLock<HashMap<String, String>>>,
// ^^^ 多线程共享需要引用计数
// ^^^^^^ 读写锁:多个读者可以同时读,写者独占
// ^^^^^^^^^^^^^^^^^^^ 实际存储
}
impl Cache {
pub fn new() -> Self {
Self {
inner: Arc::new(RwLock::new(HashMap::new())),
}
}
/// 读取缓存(可以多个 task 同时读)
pub async fn get(&self, key: &str) -> Option<String> {
let guard = self.inner.read().await;
// ^^^^^^ 获取读锁,不阻塞其他读者
guard.get(key).cloned()
}
// guard 在这里被 drop → 读锁释放
/// 写入缓存(独占访问)
pub async fn set(&self, key: String, value: String) {
let mut guard = self.inner.write().await;
// ^^^^^^^ 获取写锁,阻塞所有读者和写者
guard.insert(key, value);
}
}
我的法则:AI 生成的代码,凡是不能逐行解释的,先弄懂再用。
误区 4:觉得 AI 能替代系统学习
AI 能告诉你"Tokio 的 runtime 有 block_in_place 这个函数"。但它不能告诉你什么时候不该用它。
/// ❌ AI 告诉你的:
/// 在 async 函数里调用同步代码,用 block_in_place 就行
async fn my_async_fn() {
let result = tokio::task::block_in_place(|| {
// 同步阻塞代码
std::thread::sleep(Duration::from_secs(5));
42
});
}
/// 但 AI 不会告诉你的是:
/// 1. block_in_place 会把当前 task 移出 worker 线程
/// 2. 如果大量 task 都用 block_in_place,worker 线程池会"漏光"
/// 3. block_in_place 只应在 CPU 密集计算中使用
/// 4. 对于 IO 操作,应该用 spawn_blocking 或专门的异步 IO
系统学习 > AI 问答。对于一个新概念,我的学习路径是:
- 读官方文档的 Overview(至少前 3 页)
- 用 AI 回答"这个概念解决了什么问题"(不是"怎么用")
- 写一个最小可运行的 demo
- 故意写一个反例,看看会出什么问题
- 最后才把 AI 生成的代码放进项目
误区 5:用 AI 生成安全关键代码
/// ❌ AI 写的用户认证代码 —— 永远不要这样用!
#[derive(Deserialize)]
pub struct LoginRequest {
pub username: String,
pub password: String,
}
async fn login_handler(req: LoginRequest) -> Result<impl Reply> {
// AI 的建议:直接拼 SQL
let query = format!(
"SELECT * FROM users WHERE username = '{}' AND password = '{}'",
req.username, req.password // ← SQL 注入!
);
// 用 req.username = "admin' --" 就能绕过密码验证
sqlx::query(&query).fetch_one(&pool).await?;
Ok("登录成功")
}
安全敏感的代码,永远不要让 AI 替你写。包括但不限于:
| 类别 | 为什么不能信 AI |
|---|---|
| SQL 查询拼接 | AI 不一定会用参数化查询 |
| 密码哈希 | AI 可能建议用 MD5/SHA1 |
| JWT 验证 | AI 可能跳过签名校验 |
| 权限检查 | AI 可能把授权逻辑写在客户端 |
| 加密实现 | 永远不要自己实现加密算法 |
误区 6:高估 AI 的上下文理解能力
// 你问 AI:
// "这个函数有什么问题?帮我修复"
pub async fn process_data(config: &Config, data: &Data) -> Result<Output> {
// AI 能看到这段代码,但它不知道:
// 1. Config 的字段中哪些是热更新的
// 2. Data 在什么生命周期内有效
// 3. 调用方的并发模型是什么样的
// 4. 这个函数在整体架构中的角色
let enriched = enrich(data)?;
let transformed = transform(&config.rules, &enriched).await?;
Ok(transformed)
}
给 AI 提供上下文的最佳实践:
用户:我在维护一个多租户 SaaS 的数据处理管线。这个 process_data
函数是管线的第二步,前一步是数据清洗,后一步是存储。
当前的问题是:当租户数量从 10 增长到 1000 时,
这个函数从 50ms 变成了 3s。
[粘贴函数代码]
我在怀疑 enrich 阶段的 HashMap 在租户维度做了 O(n²) 操作。
帮我分析是否存在这个问题,以及如何修复。
误区 7:用 AI 做架构决策
AI 不能替你选数据库,不能替你决定微服务拆分,不能替你评估技术债。因为架构决策的核心不是技术优劣,而是你今天有多大的团队、明天要支持多少用户、三个月后的需求是什么——而这些信息都不在模型的训练数据里。
四、我现在的 AI 编程工作流
具体来说:
- 需求阶段:把需求描述给 AI,让 AI 用三种不同方案各写 5 行伪代码。比较思路,不比较代码细节。
- 设计阶段:自己画出数据流图或状态机图,让 AI 挑漏洞。
- 实现阶段:自己写核心逻辑。AI 帮忙生成测试用例、文档注释、CLI 参数定义。
- 审查阶段:把自己的代码给 AI,限定问题"这段代码有哪些我没考虑的边界情况?"
- 重构阶段:功能验证通过后,让 AI 提出重构建议——这时候它已经有了完整上下文。
五、总结
AI 辅助编程一年多,我对 AI 的定位经历了三次迭代:
- 第一阶段:AI 是代码生成器——"帮我写一个……"
- 第二阶段:AI 是结对编程伙伴——"这里有问题,帮我看看……"
- 第三阶段(现在):AI 是思维延伸——"我在做 X,有三种方案,帮我分析每种在 Y 场景下的 trade-off……"
关键转变是:从"让 AI 替我想"变成了"我思考,AI 检查"。的我之所以能靠自学转行,很大程度上受益于 AI。但我也看到太多人陷入"AI 写了代码 → 跑通了 → 以为自己学会了"的循环里。这不是在学习,这是在制造技术债——只不过债主是你未来的自己。
记住两件事:
- AI 写代码只节省了你 20% 的时间,但如果你不理解它写的代码,未来修 bug 会多花 200% 的时间。
- 好的开发者不是能快速写出代码的人,而是能判断"这段代码不该这么写"的人。AI 不能帮你做这个判断——只有你自己的理解能。
下一篇预告:Rust 错误处理的反面教材,半年里见过的最糟糕的错误处理代码分析。

346

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



