AI 驱动的 Rust 代码生成:从接口描述自动生成样板代码的边界探索
AI 驱动的 Rust 代码生成从接口描述自动生成样板代码的边界探索一、CRUD CRUD 还是 CRUD大家好我是一铭。我相信每个后端程序员都有这样的经历一个新项目启动第一周的工作就是——建表、写 model、写 repository、写 service、写 controller、写测试。七八个文件几百行代码全是一个模子刻出来的。Rust 更痛苦——因为它的类型系统严格一个 model 定义要配上一个 FromRow 实现、一个 NewXxx 结构体、一个 UpdateXxx 结构体、还要写一堆的impl、Serialize、Deserialize……手工写一遍无聊又容易出错。那能不能让 AI 来干这件事给它一个 OpenAPI 或者 protobuf 的接口描述文件让它自动生成 Rust 样板代码这篇文章我来探索这个方向的可能性。二、为什么不用宏macro_rules / proc_macro你可能会问Rust 不是有过程宏proc_macro吗用#[derive]自动生成不好吗好问题。宏确实能解决一部分问题但也有局限宏只能处理 Rust 内部结构无法理解 OpenAPI、SQL schema、protobuf 这些外部描述文件。宏的调试体验很差cargo expand才能看到生成的代码编译器报错位置不直观。宏无法生成上下文感知的代码——比如根据字段名推断业务逻辑、根据关联关系生成 JOIN 查询。AI 生成更灵活可以处理非标准模式比如这个接口需要加缓存、这个字段需要脱敏。AI 代码生成不是替代宏而是宏的补充——宏处理规则明确的重复代码AI 处理需要语义理解的复杂场景。三、方案一基于 OpenAPI 生成 axum handler3.1 输入OpenAPI 描述openapi: 3.0.0 info: title: 用户管理 API version: 1.0.0 paths: /users: get: summary: 获取用户列表 parameters: - name: page in: query schema: type: integer default: 1 - name: page_size in: query schema: type: integer default: 20 responses: 200: description: 用户列表 post: summary: 创建用户 requestBody: content: application/json: schema: $ref: #/components/schemas/CreateUser responses: 201: description: 创建成功 components: schemas: User: type: object properties: id: type: integer name: type: string email: type: string created_at: type: string format: date-time CreateUser: type: object required: [name, email] properties: name: type: string minLength: 1 maxLength: 50 email: type: string format: email3.2 AI 生成的 Rust 代码把上面的 OpenAPI 描述喂给 AI让它生成 Rust 代码use axum::{ extract::{Query, Json, State}, http::StatusCode, response::IntoResponse, routing::{get, post}, Router, }; use serde::{Deserialize, Serialize}; use sqlx::PgPool; use validator::Validate; // 参数校验库 use chrono::NaiveDateTime; // 数据模型 /// 用户实体数据库映射 #[derive(Debug, Serialize, sqlx::FromRow)] pub struct User { pub id: i64, pub name: String, pub email: String, pub created_at: NaiveDateTime, } /// 创建用户请求体 #[derive(Debug, Deserialize, Validate)] pub struct CreateUser { #[validate(length(min 1, max 50, message 姓名长度必须在1-50之间))] pub name: String, #[validate(email(message 邮箱格式不正确))] pub email: String, } /// 分页查询参数 #[derive(Debug, Deserialize)] pub struct Pagination { /// 页码从 1 开始 #[serde(default default_page)] pub page: i64, /// 每页大小 #[serde(default default_page_size)] pub page_size: i64, } fn default_page() - i64 { 1 } fn default_page_size() - i64 { 20 } // Handler 实现 /// GET /users - 获取用户列表分页 async fn list_users( State(pool): StatePgPool, Query(pagination): QueryPagination, ) - impl IntoResponse { // 计算偏移量 let offset (pagination.page - 1) * pagination.page_size; // 查询用户列表 总数 let users sqlx::query_as::_, User( SELECT id, name, email, created_at FROM users ORDER BY created_at DESC LIMIT $1 OFFSET $2 ) .bind(pagination.page_size) .bind(offset) .fetch_all(pool) .await; match users { Ok(users) { // 构造分页响应 let response serde_json::json!({ data: users, page: pagination.page, page_size: pagination.page_size, total: users.len() // 实际应单独查询总数 }); (StatusCode::OK, Json(response)).into_response() } Err(e) { let err serde_json::json!({ error: 查询失败, detail: e.to_string() }); (StatusCode::INTERNAL_SERVER_ERROR, Json(err)).into_response() } } } /// POST /users - 创建新用户 async fn create_user( State(pool): StatePgPool, // 使用 Json 提取器 Validate 中间件自动校验参数 Json(payload): JsonCreateUser, ) - impl IntoResponse { // 手动校验生产环境建议用 axum-valid 中间件自动校验 if let Err(errors) payload.validate() { let err serde_json::json!({ error: 参数校验失败, fields: errors.field_errors() }); return (StatusCode::UNPROCESSABLE_ENTITY, Json(err)).into_response(); } // 插入数据库 let result sqlx::query_as::_, User( INSERT INTO users (name, email) VALUES ($1, $2) RETURNING id, name, email, created_at ) .bind(payload.name) .bind(payload.email) .fetch_one(pool) .await; match result { Ok(user) { (StatusCode::CREATED, Json(user)).into_response() } Err(e) { let detail e.to_string(); let status if detail.contains(duplicate key) { StatusCode::CONFLICT } else { StatusCode::INTERNAL_SERVER_ERROR }; let err serde_json::json!({ error: 创建用户失败, detail: detail }); (status, Json(err)).into_response() } } } /// 注册路由 pub fn user_routes() - RouterPgPool { Router::new() .route(/users, get(list_users).post(create_user)) }四、方案二基于 SQL Schema 生成 Repository 层这是更常见的场景——已经有了数据库表结构需要生成 Rust 的数据访问层。4.1 输入SQL SchemaCREATE TABLE orders ( id BIGSERIAL PRIMARY KEY, user_id BIGINT NOT NULL REFERENCES users(id), product_id BIGINT NOT NULL, amount DECIMAL(10, 2) NOT NULL, status VARCHAR(20) NOT NULL DEFAULT pending, created_at TIMESTAMP NOT NULL DEFAULT NOW(), updated_at TIMESTAMP NOT NULL DEFAULT NOW() ); CREATE INDEX idx_orders_user_id ON orders(user_id); CREATE INDEX idx_orders_status ON orders(status); CREATE INDEX idx_orders_created_at ON orders(created_at);4.2 AI 生成的 Repositoryuse sqlx::{PgPool, Postgres, Transaction}; use chrono::{DateTime, Utc}; use rust_decimal::Decimal; /// 订单实体 #[derive(Debug, Clone, sqlx::FromRow, serde::Serialize, serde::Deserialize)] pub struct Order { pub id: i64, pub user_id: i64, pub product_id: i64, pub amount: Decimal, pub status: String, pub created_at: DateTimeUtc, pub updated_at: DateTimeUtc, } /// 创建订单的参数 #[derive(Debug, Deserialize)] pub struct CreateOrderParams { pub user_id: i64, pub product_id: i64, pub amount: Decimal, } /// 更新订单的参数所有字段可选只更新非空字段 #[derive(Debug, Deserialize)] pub struct UpdateOrderParams { pub status: OptionString, pub amount: OptionDecimal, } /// 订单查询条件 #[derive(Debug, Default)] pub struct OrderFilter { pub user_id: Optioni64, pub status: OptionString, pub start_date: OptionDateTimeUtc, pub end_date: OptionDateTimeUtc, pub limit: Optioni64, pub offset: Optioni64, } /// 订单数据访问层 pub struct OrderRepo; impl OrderRepo { /// 创建订单并返回完整实体 pub async fn create( pool: PgPool, params: CreateOrderParams, ) - ResultOrder, sqlx::Error { sqlx::query_as::_, Order( INSERT INTO orders (user_id, product_id, amount, status) VALUES ($1, $2, $3, pending) RETURNING id, user_id, product_id, amount, status, created_at, updated_at ) .bind(params.user_id) .bind(params.product_id) .bind(params.amount) .fetch_one(pool) .await } /// 根据 ID 查找订单 pub async fn find_by_id( pool: PgPool, order_id: i64, ) - ResultOptionOrder, sqlx::Error { sqlx::query_as::_, Order( SELECT id, user_id, product_id, amount, status, created_at, updated_at FROM orders WHERE id $1 ) .bind(order_id) .fetch_optional(pool) .await } /// 按条件查询订单列表 /// 动态构建 WHERE 子句只添加有值的条件 pub async fn find_by_filter( pool: PgPool, filter: OrderFilter, ) - ResultVecOrder, sqlx::Error { // 使用 query_builder 动态构建 SQL let mut sql String::from( SELECT id, user_id, product_id, amount, status, created_at, updated_at FROM orders WHERE 11 ); let mut params: VecString Vec::new(); let mut param_idx 1; // 动态追加条件 if let Some(ref user_id) filter.user_id { sql.push_str(format!( AND user_id ${}, param_idx)); params.push(user_id.to_string()); param_idx 1; } if let Some(ref status) filter.status { sql.push_str(format!( AND status ${}, param_idx)); params.push(status.clone()); param_idx 1; } if let Some(ref start) filter.start_date { sql.push_str(format!( AND created_at ${}, param_idx)); params.push(start.to_rfc3339()); param_idx 1; } if let Some(ref end) filter.end_date { sql.push_str(format!( AND created_at ${}, param_idx)); params.push(end.to_rfc3339()); param_idx 1; } sql.push_str( ORDER BY created_at DESC); if let Some(limit) filter.limit { sql.push_str(format!( LIMIT ${}, param_idx)); params.push(limit.to_string()); param_idx 1; } if let Some(offset) filter.offset { sql.push_str(format!( OFFSET ${}, param_idx)); params.push(offset.to_string()); } // 使用 sqlx::query 动态执行注意这里用了简单的字符串拼接 // 生产环境建议用 sqlx::QueryBuilder 防注入 let mut query sqlx::query_as::_, Order(sql); for p in params { query query.bind(p); } query.fetch_all(pool).await } /// 更新订单只更新非空字段 pub async fn update( pool: PgPool, order_id: i64, params: UpdateOrderParams, ) - ResultOptionOrder, sqlx::Error { // 构建动态 UPDATE SET 子句 let mut sets Vec::new(); let mut param_idx 1; // 用 query_builder 构建安全的参数化查询 if let Some(ref status) params.status { sets.push((format!(status ${}, param_idx), status.clone())); param_idx 1; } if let Some(ref amount) params.amount { sets.push((format!(amount ${}, param_idx), amount.to_string())); param_idx 1; } if sets.is_empty() { // 无事可更新直接返回原记录 return Self::find_by_id(pool, order_id).await; } // 总是更新 updated_at sets.push((format!(updated_at NOW()), String::new())); let set_clause: VecString sets.iter() .map(|(s, _)| s.clone()) .collect(); let sql format!( UPDATE orders SET {} WHERE id ${} RETURNING id, user_id, product_id, amount, status, created_at, updated_at, set_clause.join(, ), param_idx ); let mut query sqlx::query_as::_, Order(sql); for (_, value) in sets { if !value.is_empty() { query query.bind(value); } } query query.bind(order_id); query.fetch_optional(pool).await } }AI 代码生成的边界与现实我的实践心得AI 生成 人工审核是最佳组合。AI 生成 80% 的样板代码工程师只需要关注 20% 的业务逻辑和安全细节。Prompt 的质量决定代码的质量。描述越具体表结构、字段校验规则、错误处理策略生成的代码越贴近需求。把 AI 生成的代码当作第一版草稿而不是最终代码。编译器、clippy、集成测试是第二道防线。Process Macro 和 AI 各司其职。能用#[derive]搞定的Serialize/Deserialize/FromRow优先用宏。需要语义理解的业务逻辑、SQL 拼接、错误处理才交给 AI。实际使用中的一个意外发现AI 生成的 Repository 代码在find_by_filter里用了字符串拼接 SQL 而不是sqlx::QueryBuilder。虽然代码看起来能用但绑定参数的方式有 SQL 注入风险。这恰恰说明 AI 生成的边界——它能写出看起来正确的代码但不能保证安全。编译通过只是及格线clippy lint 安全审计才是上线标准。五、总结从 OpenAPI 生成 axum handler数据模型、参数校验、路由注册一气呵成。从 SQL Schema 生成 Repository 层完整的 CRUD、动态查询、分页排序。AI 代码生成不是要取代程序员而是要消灭那些浪费程序员生命的重复劳动。Rust 的类型系统严格恰好是 AI 代码生成的好搭档——编译器会在编译期把所有类型错误揪出来AI 写错的代码根本过不了编译。这就是我目前探索到的边界——AI 可以帮我省掉 70% 的重复琐碎工作但架构决策、业务建模、安全把控这些核心能力暂时还是工程师的独特价值。未来随着 AI Agent 能力增强从需求文档到可运行代码的全自动流程也许真的不远了。但在那一天到来之前我们要做的是用好 AI 工具把精力花在更有创造性的工作上。有什么想法欢迎评论区交流

相关新闻

05-职业发展-AI时代数字孪生开发者进阶指南从技术执行者到价值创造者

05-职业发展-AI时代数字孪生开发者进阶指南从技术执行者到价值创造者

AI时代数字孪生开发者进阶指南:从技术执行者到价值创造者的转型之路 引言 在AI技术汹涌来袭的时代背景下,数字孪生开发者面临着前所未有的职业发展挑战。传统的"写代码-做项目-交付"的线性发展路径正在被打破,取而代之的是"技…

2026/10/10 14:24:37 阅读更多 →
网站自动建站备份工具:本地测试站,1分钟平滑传阿里云

网站自动建站备份工具:本地测试站,1分钟平滑传阿里云

普通办公电脑上的网页文件传到云端服务器,过去靠手动上传。现在用软件打包。本地运行环境装有PHP 8.1版本的电脑里,存放着500MB的网页图片。传到拥有2核4G配置的阿里云轻量应用服务器,整个动作在60秒内完成。文件传输速度能达到每秒15MB。普通…

2026/10/9 11:02:25 阅读更多 →
AI Agent面试题第二期(持续更新)

AI Agent面试题第二期(持续更新)

一、Python基础&工程能力(10题) ### 基础题1. 简历提到你使用过**生成器、装饰器、上下文管理器、匿名函数**,分别说说它们的作用,结合你的Streamlit流式输出项目举例说明生成器怎么解决回答丢失问题? 生成器作用&…

2026/10/12 22:13:38 阅读更多 →

最新新闻

Qwen-Image-2.1云端部署实战:从环境搭建到API服务

Qwen-Image-2.1云端部署实战:从环境搭建到API服务

前段时间我把阿里开源的 Qwen-Image-2.1 图像生成模型部署到了云端的 GPU 服务器上,从裸机到能跑通图片生成,前后踩了不少坑,也把整个流程理顺了。这篇文章就是把我在云端部署 Qwen-Image-2.1 的完整过程整理成一份能直接照着操作的保姆级教程…

2026/10/13 8:13:33 阅读更多 →
2026继续教育学员必看:降AI率工具测评与实操指南

2026继续教育学员必看:降AI率工具测评与实操指南

2026继续教育学员都在问的“降AI率”到底怎么回事写这篇稿子之前,我刚帮一位备考社工证继续教育的朋友看过结课论文,系统直接标红“疑似AI生成比例48%”。这位朋友白天上班晚上带娃,论文初稿确实是靠AI辅助两天赶出来的,看到这个数…

2026/10/13 8:13:33 阅读更多 →
AnyPS5实战:手把手教你用任意屏幕畅玩PS5串流

AnyPS5实战:手把手教你用任意屏幕畅玩PS5串流

很多人一听到“AnyPS5”这个项目名,第一反应是“这是不是要把 PS5 搞成什么模拟器全家桶”。其实不是,我做这个项目的初衷特别朴素:让手头任何一块屏幕,都能变成 PS5 的显示器。客厅电视被家人霸占、卧室里想躺着玩、出差时想打开…

2026/10/13 8:13:33 阅读更多 →
REA建模实践:用资源-事件-代理人模型重构进销存系统

REA建模实践:用资源-事件-代理人模型重构进销存系统

拿到这个标题时,很多人会问“rea是什么”。它不是某个产品缩写,也不是新造的网络热词,而是我最近重构一个进销存系统时顺手做的小工具名。当时我已经被数据库里孤零零的订单表、发货表、收款表搞到崩溃——业务上清清楚楚的一条“卖货收钱”&…

2026/10/13 8:13:33 阅读更多 →
移动端射击游戏性能优化实战:UE引擎帧率与渲染取舍

移动端射击游戏性能优化实战:UE引擎帧率与渲染取舍

1. 移动端高性能射击游戏的设计取舍与目标定位《三角洲行动》移动端这件事,圈内讨论一直很热。作为参与过几款FPS/TPS移动端项目的老兵,我想从实际开发落地的角度,把这套打法拆开揉碎讲清楚。很多团队拿到UE引擎就急着堆功能,结果…

2026/10/13 8:13:33 阅读更多 →
goInception:MySQL 审核与回滚工具实战

goInception:MySQL 审核与回滚工具实战

简介:goInception 是一款集 SQL 审核、执行、备份与回滚语句生成于一体的 MySQL 运维工具,面向数据库管理员、后端开发与运维工程师,尤其适合需要规范 SQL 上线流程、降低误操作风险的团队。它通过对 SQL 进行语法解析,按自定义规…

2026/10/13 8:12:33 阅读更多 →

日新闻

物联网系统集成避坑指南:从选型到验收的关键要点

物联网系统集成避坑指南:从选型到验收的关键要点

做了这么多年物联网系统集成的项目,我最大的感受是,大部分坑不是出在技术前沿,而是出在一些看起来很平常的地方。设备选了便宜的、协议没问清楚、现场没摸透、验收标准写得含糊——这些细枝末节,最后都会变成延期、超支和扯皮的大…

2026/10/13 0:00:57 阅读更多 →
ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

ESP32舵机控制全攻略:从PWM原理到高级玩法与常见故障排查

如果你玩ESP32有一阵子了,很难不碰到舵机控制这道坎。做智能小车要转方向,做机械臂要抬关节,做云台要偏航俯仰,背后都是同一个东西在干活:舵机。而我见过太多人卡在“代码能编译、舵机就是不听话”这个阶段&#xff0c…

2026/10/13 0:00:57 阅读更多 →
CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

CAN总线原理与STM32实战:抗干扰、仲裁、错误处理全解析

1. 为什么CAN总线至今仍是工业现场的“硬通货”?——从汽车底盘到智能温室的真实需求你有没有拆过一辆老款燃油车的中控台?或者调试过某高校实验室里那台跑着Modbus协议却频频丢帧的PLC控制柜?又或者,在某个农业物联网项目里&…

2026/10/13 0:00:57 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 21:05:09 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 14:48:49 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →