Rust中无需可变models实现异步加载?可借助Stream吗?
在Rust中无需可变
models实现批量异步加载的方案? 问题背景
能不能不用把models声明成可变类型来完成批量加载?能不能用Stream实现?
用uuids.iter().map(...)的核心障碍有两个:
- 无法安全地将
&mut conn传入闭包 DatabaseModel::load是异步方法
现有实现(需要可变models):
// 假设: // uuids: Vec<uuid::Uuid> // conn: &mut PgConnection(来自sqlx) let mut models = Vec::<DatabaseModel>::new(); for uuid in &uuids { let model = DatabaseModel::load(conn, uuid).await; models.extend(model); } //.. 后续对models做不可变操作
理想的简化写法(无需可变models):
let models = uuids.iter().map(|uuid| DatabaseModel::load(uuid));
解决方案
完全可以做到不用可变models,而且确实可以借助Stream实现,核心是用异步迭代器处理异步任务,同时遵守Rust的引用规则。
方案1:串行加载(适配单个&mut conn)
直接用futures crate的Stream工具把普通迭代器转成异步流,串行处理每个UUID,最后一次性收集结果到不可变的models中。
首先需要在Cargo.toml中引入依赖:
[dependencies] futures = "0.3" sqlx = { version = "0.7", features = ["postgres", "uuid", "runtime-tokio-native-tls"] }
实现代码:
use futures::stream::{self, StreamExt}; // 假设: // uuids: Vec<uuid::Uuid> // conn: &mut PgConnection(来自sqlx) let models: Vec<DatabaseModel> = stream::iter(&uuids) // 串行处理每个UUID,确保&mut conn不会被同时借用 .then(|uuid| DatabaseModel::load(conn, uuid)) // 收集所有异步结果为Vec,await完成后models就是不可变的 .collect() .await; // 后续直接使用不可变的models即可
方案2:并行加载(需连接池)
如果想要提升效率并行加载,不能用单个&mut conn,得改用sqlx::PgPool连接池,这样每个加载任务可以独立获取连接:
use futures::stream::{self, StreamExt}; // 假设已初始化连接池 let pool: sqlx::PgPool = /* 初始化逻辑 */; let models: Result<Vec<DatabaseModel>, sqlx::Error> = stream::iter(&uuids) .map(|uuid| async move { // 从池里获取连接 let conn = pool.acquire().await?; DatabaseModel::load(&conn, uuid).await }) // 同时处理最多10个请求,可根据需求调整 .buffer_unordered(10) .collect() .await; // 处理结果(这里models是Result,需要unwrap或match) let models = models?;
关键说明
- 直接用
iter().map()不行的原因:普通迭代器不支持异步闭包,且&mut conn的独占性不允许多个异步任务同时持有它。 - 串行方案中
then方法会确保上一个异步任务完成后再处理下一个,完美适配单个可变连接的场景。 - 最终得到的
models从一开始就是不可变的,完全符合你的需求。
内容的提问来源于stack exchange,提问作者Awesome-o
相关产品推荐
相关产品推荐

