用途
该技能面向 Turso 核心代码的分配器迁移工作,尤其适用于以下场景:将标准堆集合替换为 crate::alloc 别名,或为分配路径增加可失败(fallible)分配能力。
核心规则
- 在迁移模块中使用 crate::alloc::*。
- 保持调用点名称稳定:Vec、Box、HashMap、BTreeSet、Arc 及类似标准名称。
- 新的分配器专用名称使用 Turso,而非 Limbo。
- nightly 分配器行为使用 cfg(nightly),而非 Cargo feature。
- 除非用户明确要求,不要修改 set_allocator。
- 目前 Arc 与 Weak 继续经由 crate::sync 路由,以兼容 shuttle;分配器感知的共享指针留作 TODO。
- 除非测试阻塞编译或用户要求,不要将测试迁移到可失败分配。
导入模式
对于迁移模块,优先使用单一分配器命名空间导入:
use crate::alloc::*;
然后移除冲突的导入,例如:
use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet, VecDeque};
use std::rc::Rc;
use std::sync::Arc;
use std::vec::Vec;
use crate::sync::Arc;
仅在未迁移类型仍需要标准库集合的狭窄边界处,保留显式的 std::... 导入。
构造器
在使用 use crate::alloc::* 之后,使用常规构造器名称:
Vec::new()
Vec::with_capacity(capacity)
HashMap::new()
BTreeSet::new()
Box::new(value)
Arc::new(value)
分配器扩展 trait 在底层按需提供分配器感知的构造器。
当某个方法变为可失败时,优先更改返回类型,而不是重命名方法:
pub fn new(...) -> Result<Self>
不要仅因为返回类型改变就把方法重命名为 try_new,除非本地 API 已使用该模式或用户要求。
可失败分配
在返回 Result、或可合理改为返回 Result 的迁移生产代码中,使用可失败分配 API:
try_vec![a, b, c]?
vec.try_push(value)?
vec.try_extend(iter)?
vec.try_reserve(additional)?
Vec::try_with_capacity(capacity)?
iter.try_collect::<Vec<_>>()?
尽可能直接使用 ?。TryReserveError 会转换为 LimboError,因此避免重复的手动错误映射。
当 try_with_capacity 或 try_reserve 已处理容量失败时,不要添加手动容量溢出检查。在添加检查或重构分配逻辑之前,先与之前的代码进行比较。
迭代器收集
使用 TursoIteratorExt::try_collect 进行分配器感知的收集。
支持的模式包括普通集合、Option 和 Result:
let values: Vec<_> = iter.try_collect()?;
let maybe_values: Option<Vec<_>> = iter_of_options.try_collect()?;
let result_values: Result<Vec<_>, E> = iter_of_results.try_collect()?;
对于无效项应产生 None 的解析场景,优先使用:
let parsed = input
.split_whitespace()
.map(|s| s.parse::<u64>().ok())
.try_collect()?;
String 与标准库边界
String 在 stable 上并非完全分配器参数化。当迁移代码仍执行字符串格式化、to_string、format! 或 join 时,除非用户要求,不要过度迁移。在有用时添加聚焦的 TODO:
// TODO: make this formatting allocation fallible once String is allocator-aware
// in the Turso allocation namespace.
某些中心类型在单独迁移之前可能仍使用标准集合,尤其是:
- Value::Blob
- record payloads
- 共享指针内部
- 外部 API 表面
在这些边界处,显式的 std::vec::Vec 转换是可接受的。保持它们狭窄,避免将标准集合导入扩散到迁移模块中。
迁移工作流
- 检查模块及附近的调用方。
- 在简化检查或更改容量计算之前,与之前的代码进行比较。
- 添加 use crate::alloc::*。
- 移除冲突的标准集合与指针导入。
- 转换构造器与变更路径。
- 在适当处让承载分配的函数返回 Result。
- 在生产代码中一致使用可失败 API。
- 除非必要,保持测试不可失败。
- 检查格式化与编译。
- 为每个连贯的迁移切片提交。
提交边界
将基础分配器工作与迁移分开。
基础提交包括:
- core::alloc 中的别名
- 扩展 trait
- try_vec!
- try_collect
- TryReserveError 转换
- 分配器后端设置
迁移提交应聚焦于模块,例如:
- RowSet
- VDBE arrays
- sorter
- stats
- schema
- statement
- virtual tables
在每个连贯切片后频繁提交。
验证
对于普通迁移切片,检查格式化与编译:
cargo fmt
cargo check -p turso_core
在分配器敏感提交之前,还要检查 nightly cfg 构建:
RUSTFLAGS="--cfg nightly" cargo +nightly check -p turso_core
对于分配器集合性能工作,使用 Divan 对比脚本。它默认运行 alloc_collections 基准,并打印排序后的 std 与 Turso 对比表:
scripts/compare-divan-std-turso.py --run
要运行 nightly 分配器路径,执行:
scripts/compare-divan-std-turso.py --run --nightly
有用的变体:
scripts/compare-divan-std-turso.py --run --bench-filter hash_map --filter hash_map
scripts/compare-divan-std-turso.py --run --save-output /tmp/alloc_collections.txt
scripts/compare-divan-std-turso.py /tmp/alloc_collections.txt
仅当触及的代码路径有风险、行为发生变化,或编译不足以验证迁移时,才运行聚焦测试。
适用场景与限制
该技能适用于 Turso 核心代码的分配器迁移,包括将标准堆集合替换为 crate::alloc 别名、增加可失败分配路径、以及分配器集合性能对比。限制方面:String 在 stable 上并非完全分配器参数化,相关格式化代码不应过度迁移;某些中心类型与外部 API 表面在单独迁移前仍使用标准集合;测试默认不迁移到可失败分配;set_allocator 除非用户明确要求否则不修改。