rust-lifetime-complex
SKILL.md
高级生命周期与类型系统
核心问题
为什么这个类型转换就是编译不过?
类型系统的边界往往出乎意料。
HRTB + dyn Trait Object 冲突
问题代码
// ❌ HRTB 不能装进 dyn trait object
pub type ConnFn<T> =
dyn for<'c> FnOnce(&'c mut PgConnection) -> BoxFuture<'c, T> + Send;
let f = Box::new(move |conn: &mut PgConnection| -> BoxFuture<'_, i64> {
Box::pin(async { Ok(42) })
}) as Box<ConnFn<i64>>; // ❌ "one type is more general than the other"
原因
- 闭包有具体生命周期
'_ ConnFn<T>要求任意生命周期for<'c>- 具体不能自动变成通用
解决:把 HRTB 留在函数边界
// ✅ HRTB 只在调用点使用泛型
impl Db {
pub async fn with_conn<F, T, Fut>(&self, f: F) -> Result<T, DbError>
where
F: for<'c> FnOnce(&'c mut PgConnection) -> Fut + Send,
Fut: Future<Output = Result<T, DbError>> + Send,
{
let mut conn = self.pool.acquire().await?;
f(&mut conn).await
}
}
使用
db.with_conn(|conn| async move {
// 这里 'c 由调用时确定,不需要 dyn
sqlx::query("...").fetch_all(conn).await
}).await
GAT + dyn Trait Object
问题代码
// ❌ GAT 不能和 dyn Trait 一起用
trait ReportRepo: Send + Sync {
type Row<'r>: RowView<'r>; // ❌ GAT
}
let repo: Arc<dyn ReportRepo> = ...; // ❌ 编译错误
错误信息
error[E0038]: the trait cannot be made into an object
because associated type `Row` has generic parameters
原因
dyn ReportRepo需要统一大小Row<'r>对不同'r有不同大小- 对象安全规则禁止
解决:分层架构
// 内部:GAT + 借用(高性能)
trait InternalRepo {
type Row<'r>: RowView<'r>;
async fn query<'c>(&'c self) -> Vec<Self::Row<'c>>;
}
// 外部:owned DTO(兼容 GraphQL)
pub trait PublicRepo: Send + Sync {
async fn query(&self) -> Vec<ReportDto>; // owned
}
// 适配层
impl PublicRepo for PgRepo {
async fn query(&self) -> Vec<ReportDto> {
let rows = self.internal.query().await; // 借用内部
rows.into_iter().map(|r| r.to_dto()).collect()
}
}
架构图
GraphQL Layer (需要 'static)
↓
PublicRepo Trait (owned)
↓
Adapter (转换 borrowed → owned)
↓
InternalRepo Trait (GAT, borrowed)
↓
DB Implementation
'static 要求冲突
场景
// async-graphql 要求 schema 是 'static
// 但 repo 方法返回借用数据
async fn resolve(&self) -> Result<&'r Row<'r>> {
// ❌ 'r 不能 outlive 'static
}
解决:owned 数据
// 不要在 API 层暴露借用
async fn resolve(&self) -> Result<ReportDto> {
let row = self.repo.query().await?; // owned
Ok(row.to_dto())
}
何时例外
- 仅在 非常短期 的借用(如单次函数调用内)
- 完全控制 所有者和借用的生命周期
- 性能收益显著 且不可替代
常见冲突模式
| 模式 | 冲突原因 | 解决 |
|---|---|---|
| HRTB → dyn | 具体 vs 通用 | 泛型函数替代 dyn |
| GAT → dyn | 大小不固定 | 分层设计 |
| 'static + 借用 | 生命周期矛盾 | owned 数据 |
| async + 生命周期 | 协程持有状态 | drop 借用或 owned |
| closure 捕获 + Send | 生命周期问题 | 克隆或 'static |
何时放弃借用
性能 vs 可维护性
// 性能收益 vs 复杂性
fn should_borrow() -> bool {
// 大数据结构 → 借用
// 高频访问 → 借用
// 生命周期简单 → 借用
// 复杂生命周期 → owned
// API 边界 → owned
// 异步上下文 → owned
}
经验法则
- API 层:默认 owned
- 内部实现:按需借用
- 性能热点:考虑借用
- 复杂度高时:退回到 owned
调试技巧
编译器错误
| 错误 | 含义 |
|---|---|
| "one type is more general" | 试图把具体转通用 |
| "lifetime may not live long enough" | 借用超出范围 |
| "cannot be made into an object" | GAT + dyn 不兼容 |
| "does not live long enough" | 借用被提前释放 |
方法
- 最小化:写出最小复现代码
- 标注生命周期:显式写出所有生命周期
- 逐步简化:去掉抽象,直面问题
- 接受现实:不是所有设计都能编译
Weekly Installs
3
Repository
huiali/rust-skillsGitHub Stars
20
First Seen
Jan 30, 2026
Security Audits
Installed on
trae-cn2
claude-code2
codebuddy2
trae1
qoder1
antigravity1