- 必须在 crate 根目录定义统一的错误类型(如
KernelError)
- 必须建立清晰的错误层次结构,各模块错误应能通过
From trait 统一转换
- 禁止在库代码中使用
anyhow::Result 作为公开 API 返回类型,应使用 thiserror 定义类型化错误
- 禁止对错误类型实现
From<anyhow::Error> 的 blanket 实现,这会抹除结构化错误信息
#![allow(unused)]
fn main() {
// 推荐:使用 thiserror 定义清晰的错误枚举
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum KernelError {
#[error("Agent error: {0}")]
Agent(#[from] AgentError),
#[error("Config error: {0}")]
Config(#[from] ConfigError),
// ...
}
}
- 必须为公开枚举添加
#[non_exhaustive] 属性,保证向后兼容
#![allow(unused)]
fn main() {
#[derive(Debug, Clone)]
#[non_exhaustive]
pub enum AgentState {
Idle,
Running,
// 未来可安全添加新变体
}
}
- 可比较/测试的类型 必须 派生
PartialEq、Eq
- 调试输出类型 必须 派生
Debug,对于无法自动派生的字段应手动实现
- 序列化类型 必须 派生
Clone(除非有特殊理由)
- 禁止在同一 crate 内定义同名类型表示不同概念
- 检查清单:
AgentConfig、AgentEvent、TaskPriority 等核心类型名称
- 必须使用
pub(crate) 限制内部模块可见性
- 必须通过
lib.rs 或 prelude 精心设计公开 API 面板
- 禁止将所有模块直接
pub mod 导出
#![allow(unused)]
fn main() {
// lib.rs 推荐 structure
pub mod error;
pub mod agent;
pub use error::KernelError;
pub use agent::{Agent, AgentContext};
mod internal; // 内部实现
}
- 应该提供 crate 级别的 prelude 模块,聚合常用类型
#![allow(unused)]
fn main() {
// src/prelude.rs
pub use crate::error::KernelError;
pub use crate::agent::{Agent, AgentContext, AgentState};
// ...
}
- Rust 1.75+ 环境 应该 使用原生
async fn in trait,替代 #[async_trait]
- 仅在真正需要异步的方法上使用 async,同步操作不应标记为 async
- 正则表达式等编译成本高的对象 必须 使用
LazyLock 或 OnceLock 缓存
#![allow(unused)]
fn main() {
use std::sync::LazyLock;
static ENV_VAR_REGEX: LazyLock<Regex> = LazyLock::new(|| {
Regex::new(r"\$\{([^}]+)\}").unwrap()
});
}
- 时间戳生成逻辑 必须 抽象为单一工具函数
- 应该 提供可注入的时钟抽象用于测试
#![allow(unused)]
fn main() {
pub trait Clock: Send + Sync {
fn now_millis(&self) -> u64;
}
pub struct SystemClock;
impl Clock for SystemClock {
fn now_millis(&self) -> u64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64
}
}
}
- 禁止 手写 Base64、加密算法等已有成熟实现的逻辑
- 优先使用社区广泛验证的 crate
- 禁止在可通过泛型约束的场景滥用
serde_json::Value
- 避免使用
Box<dyn Any + Send + Sync> 作为通用存储,优先考虑泛型或 trait object with specific trait
- trait 方法签名中
&self 与 &mut self 的选择 必须 保持一致性
- 若内部需要通过
&self 修改状态(如 Arc<RwLock<_>>),应在文档中明确说明副作用
- 构造函数参数类型 应该 统一:优先
impl Into<String> 或 &str
#![allow(unused)]
fn main() {
// 推荐
pub fn new(id: impl Into<String>) -> Self { ... }
// 避免
pub fn new(id: String) -> Self { ... }
}
- Builder 方法 必须 对无效输入进行校验或返回 Result
#![allow(unused)]
fn main() {
pub fn with_weight(mut self, weight: f64) -> Result<Self, &'static str> {
if weight < 0.0 {
return Err("Weight must be non-negative");
}
self.weight = Some(weight);
Ok(self)
}
}
- 禁止 自定义方法名与标准 trait 方法名冲突(如
to_string_output 与 to_string)
- 必须 为手动实现的
Ord trait 编写完整测试覆盖所有分支
- 推荐使用
derive 或基于判别式的简化实现
#![allow(unused)]
fn main() {
// 避免
let ts = as_millis() as u64;
// 推荐
let ts = u64::try_from(as_millis()).unwrap_or(u64::MAX);
}
#![allow(unused)]
fn main() {
#[derive(Serialize, Deserialize)]
struct MessageEnvelope {
version: u8,
payload: Vec<u8>,
}
}
#![allow(unused)]
fn main() {
pub trait Serializer: Send + Sync {
fn serialize<T: Serialize>(&self, value: &T) -> Result<Vec<u8>>;
fn deserialize<T: DeserializeOwned>(&self, data: &[u8]) -> Result<T>;
}
}
- 必须 包含:边界值、空值、无效输入、并发场景
- 禁止 仅测试 happy path
- 必须 为核心逻辑编写单元测试
- 应该 编写模块间交互的集成测试
- 外部依赖(时钟、随机数、网络)必须 可通过 trait 注入 mock 实现
- 被 feature gate 的依赖 必须 在 Cargo.toml 中标记
optional = true
- 禁止 feature gate 部分代码但依赖仍被无条件编译
[dependencies]
config = { version = "0.14", optional = true }
[features]
default = []
config-loader = ["dep:config"]
| 检查项 | 要求 | 状态 |
公开枚举是否 #[non_exhaustive] | 必须 | ☐ |
| 公开错误类型是否统一 | 必须 | ☐ |
| 是否存在同名不同义的类型 | 禁止 | ☐ |
| trait 是否存在 async 不必要使用 | 检查 | ☐ |
| 数值转换是否有溢出风险 | 检查 | ☐ |
| 时间相关代码是否可测试 | 必须 | ☐ |
| Builder 是否有输入验证 | 必须 | ☐ |
| 正则等是否使用缓存 | 必须 | ☐ |
| 是否有集成测试 | 应该 | ☐ |
| 错误路径测试覆盖 | 必须 | ☐ |