当设计或修改 REST API 响应结构、处理 API 返回值时触发。防止 API 设计缺陷导致的字段错位、类型歧义等问题。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Rust 开发规范(聚焦 Axum + Tokio 后端栈)。当用户操作 .rs、Cargo.toml、Cargo.lock 文件, 或涉及 Rust 后端开发(Axum、Tokio、sqlx、reqwest)时触发。包含工具链、命名约定、错误处理、异步陷阱、模块组织、依赖管理、测试规范、unsafe 红线等。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
설치 방법 보기설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.
参考来源:Rust API Guidelines、Rust Async Book、tokio.rs、Axum 官方示例、Effective Rust
适用范围:后端 Web 服务(Axum + Tokio 路线)。CLI/wasm/嵌入式不在本规范覆盖范围内。
# 格式化
cargo fmt --all
# 静态检查,warning 当 error
cargo clippy --all-targets -- -D warnings
# 测试
cargo test --all-features
# CVE 扫描(独立安装)
cargo audit
# 许可证/重复依赖检查(独立安装)
cargo deny check
# 检测未使用依赖
cargo machete
edition:新项目默认 edition = "2024",老项目保持 2021 不强迁。
版本基线(写死,避免重复决策):
1.x,Axum 0.8,tower-http 0.61、tracing 0.1、tracing-subscriber 0.30.8(runtime-tokio-rustls)| 类型 | 规则 | 示例 |
|---|---|---|
| 模块/文件 | snake_case | user_service.rs |
| 函数/变量 | snake_case | fetch_user, db_pool |
| 类型/Trait/Enum | UpperCamelCase | UserRepo, AppError |
| 常量/静态 | SCREAMING_SNAKE_CASE | MAX_RETRY |
| 泛型 | 单大写或 UpperCamelCase | T, Req, Resp |
| 生命周期 | 短小写 | 'a, 'ctx |
禁止:common、util、helper、base 等无意义模块名。模块按职责命名(api、db、service)。
参照 apple-store-price-rs / org-site-backend 的实证拆分,按职责切文件,不堆 lib.rs:
src/
├── main.rs # 仅 bootstrap:日志初始化、读配置、建路由、启服务
├── config.rs # 配置加载(env / toml)
├── error.rs # AppError + IntoResponse impl
├── db.rs # 数据库连接池/迁移
├── api.rs # 路由聚合 + handler
├── service/ # 业务逻辑(无副作用层)
│ └── user.rs
└── models.rs # 领域类型 + serde 派生
可见性:pub(crate) 优先于 pub。只在确实要对外暴露时才用 pub。
分场景选边:
| 场景 | 选择 | 理由 |
|---|---|---|
| 库 crate | thiserror | 调用方需要按变体匹配 |
| 二进制应用顶层 | anyhow | 只关心"成功/失败 + 上下文" |
| 跨边界(handler / 公共 API) | 自定义 AppError + IntoResponse | 控制对外错误形态 |
禁止生产代码出现:
.unwrap() / .expect()(仅测试和 const 上下文可用)panic!()(仅用于"逻辑上不可达"且带 SAFETY 注释)clippy 配置(在 Cargo.toml 或 clippy.toml):
[lints.clippy]
unwrap_used = "deny"
expect_used = "deny"
panic = "deny"
错误传播用 ?,跨类型用 From 实现自动转换:
#[derive(thiserror::Error, Debug)]
pub enum AppError {
#[error("db error: {0}")]
Db(#[from] sqlx::Error),
#[error("not found")]
NotFound,
#[error("bad request: {0}")]
BadRequest(String),
}
详见 references/async-axum.md 的 IntoResponse 实现。
1. 阻塞调用必须丢到 spawn_blocking
// ❌ 在 async fn 里直接调用阻塞 API
let data = std::fs::read("big.bin")?;
// ✅
let data = tokio::task::spawn_blocking(|| std::fs::read("big.bin")).await??;
判定标准:CPU 密集 > 10ms、std::fs、std::thread::sleep、同步 DB driver、reqwest::blocking —— 都必须 spawn_blocking。
2. 不要跨 .await 持有 std::sync::Mutex
// ❌ 编译能过,运行时死锁/性能崩溃
let g = std_mutex.lock().unwrap();
do_async().await;
// ✅ 用 tokio::sync::Mutex
let g = tokio_mutex.lock().await;
do_async().await;
3. Rc / RefCell 不能跨 .await:违反 Send 边界,编译报错,改用 Arc / Mutex。
4. tokio::select! 分支必须取消安全
tokio::time::sleep、channel.recv、tokio::io::AsyncRead 标记方法async fn(取消时可能丢数据)不确定时,把状态保存到 select! 外部变量,分支只读写本地变量。
5. 优雅停机:用 tokio::signal::ctrl_c() + axum::serve(...).with_graceful_shutdown()。
更多陷阱见 references/async-axum.md。
函数参数:
| 想要 | 用 | 不要用 |
|---|---|---|
| 只读字符串 | &str | &String |
| 只读切片 | &[T] | &Vec<T> |
| 路径 | &Path | &PathBuf / &str |
| 字符串入参可能多种来源 | impl AsRef<str> | 多个重载 |
共享决策树:
需要跨线程共享?
├── 否 → 用 Box<T> 或值传递
└── 是 → Arc<T>
├── 不可变共享 → Arc<T>
├── 读多写少 → Arc<RwLock<T>>
└── 读写均衡 → Arc<Mutex<T>>
Clone 不是坏事,但循环内 Clone 大对象要警觉,考虑 Arc 或 Cow<'_, T>。
type AppState = Arc<InnerState>;
let app = Router::new()
.nest("/api/users", user::routes()) // 按模块拆 + nest
.layer(TraceLayer::new_for_http()) // 日志中间件
.layer(TimeoutLayer::new(Duration::from_secs(30)))
.with_state(state);
Arc 包装,不要克隆裸结构体IntoResponse,统一对外形态(status + json body)fn routes() -> Router<AppState>,避免 main.rs 膨胀tower-http:CORS / 超时 / 限流 / 日志 / 静态文件详见 references/async-axum.md。
版本指定:Cargo.toml 默认 caret("1" 等价 ^1),不要锁死 =1.2.3 除非有兼容性问题。
features 显式声明,禁止盲依赖默认值:
# ✅ 显式开启需要的 feature
tokio = { version = "1", features = ["macros", "rt-multi-thread", "signal"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
TLS 选 rustls,避免 openssl(跨平台编译麻烦)。
升级前先 dry-run:
cargo update --dry-run
# 独立安装
cargo outdated
Cargo.lock 二进制项目必须提交,库项目不提交。
// 异步测试用 #[tokio::test]
#[tokio::test]
async fn fetches_user() {
let svc = test_service().await;
let user = svc.find(1).await.unwrap(); // 测试代码可以 .unwrap()
assert_eq!(user.name, "alice");
}
// 表驱动:可选用 rstest crate
#[rstest::rstest]
#[case(1, 2, 3)]
#[case(-1, 1, 0)]
fn adds(#[case] a: i32, #[case] b: i32, #[case] expected: i32) {
assert_eq!(a + b, expected);
}
#[cfg(test)] mod tests { ... } 紧贴被测代码tests/ 顶层目录,每个文件独立 crate.unwrap()(失败即测试失败,正是预期)业务代码原则禁用 unsafe。
如必须使用(FFI / 极端性能场景):
// SAFETY: ... 注释,说明保持的不变量unsafe 块到最小范围| 陷阱 | 解决 |
|---|---|
循环内 format! / 字符串拼接 | 用 String::with_capacity + write! |
| Vec 频繁扩容 | Vec::with_capacity(n) |
循环内 .clone() 大对象 | 借用或 Arc |
Box<dyn Trait> 滥用 | 优先泛型 <T: Trait>(静态分发) |
String 当 key | 优先 &str 或 Cow<'static, str> |
| 阻塞 IO 在 async 里 | spawn_blocking(见异步章节) |
性能分析:
cargo build --release
# 火焰图(独立安装)
cargo flamegraph
# criterion 基准测试
cargo bench
| 文件 | 内容 |
|---|---|
references/rust-style.md | 命名细则、From/TryFrom 规范、newtype 模式、迭代器优先 |
references/async-axum.md | spawn_blocking 边界、select! 取消安全、tower 中间件组合、IntoResponse 模式、shutdown 信号 |
📋 本回复遵循:
rust-dev- [具体章节]
아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.
로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.
아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.
当设计或修改 REST API 响应结构、处理 API 返回值时触发。防止 API 设计缺陷导致的字段错位、类型歧义等问题。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
当设计或修改 REST API 响应结构、处理 API 返回值,或生成 Excel/CSV/PDF/对账文件等下游产物时触发。防止 API 设计缺陷导致的字段错位、类型歧义,以及生成产物时关键字段缺失但静默成功的问题。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
网关/代理/WAF/CDN 中间件的安全关键词匹配实现规范,防止纯子串匹配误判正常响应内容中的技术术语(如 Cloudflare、502、error)
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
当 API/任务可能执行超过 10 秒(批量数据处理、远程 API 批量调用、全表扫描、跨租户聚合)时触发。防止同步接口被网关 30s 超时切断、用户重复点击触发并发、状态缓存内存泄漏等问题。提供异步任务状态机标准模板。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
当用户操作 .sh、Dockerfile、Makefile、.yml、.yaml 文件,或在 Markdown 中编写 bash 代码块时触发。提供 Bash 编写规范。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.
Bash 脚本与系统命令规范。禁止行尾注释,强制使用 tee/heredoc 写入,提升命令执行的可维护性。
한국어 요약은 준비 중입니다. 원문 설명을 표시하고 있습니다.