Rust异步函数取出Vec<Client>时遭遇生命周期错误求助
123 | }
| -
| |
| completion_queues dropped here while still borrowed
| borrow might be used here, when out is dropped and runs the Drop code for type Vec
|
= note: values in a scope are dropped in the opposite order they are defined
运行环境为Tokio,`ClientConfig`和`Client`是自定义结构体,`ProtectionDomain`与`Context`来自RDMA库,相关代码如下: ## 异步函数代码 ```rust pub async fn connect_to_clients<'a>( config: ClientConfig, ctx: Arc<Context>, pd: Arc<ProtectionDomain<'_>>, mut out: Vec<Client<'a>>, ) { info!("Running client mode."); let completion_queues: Vec<CompletionQueue> = config .targets .iter() .enumerate() .map(|(i, _)| ctx.create_cq(1, i.try_into().unwrap()).unwrap()) .collect(); let mut i = 0; for c in config.targets { out.push(Client::new(&pd, &completion_queues[i], c.clone(), config.frame_size).await); i += 1; } }
Config.rs代码
use serde::Deserialize; #[derive(Deserialize, Debug)] pub struct ClientConfig { pub targets: Vec<String>, pub frame_size: usize, }
client.rs代码
pub struct Client<'a> { qp: QueuePair<'a>, memory_manager: MemoryManager, }
MemoryManager代码
pub struct MemoryManager { pub mr: Arc<Mutex<MemoryRegion<u8>>>, }
问题根源
completion_queues是函数内部创建的局部变量,生命周期仅限当前函数作用域,但Client实例持有了它的引用。而传入的out向量生命周期'a远长于completion_queues,函数结束时completion_queues被销毁,Client的引用就会变成悬空引用,触发生命周期错误。
解决步骤
核心思路是让CompletionQueue的生命周期与Client保持一致,最常用的方案是用Arc包裹CompletionQueue,让Client持有Arc而非裸引用:
修改
Client结构体:
调整Client定义,让它持有Arc<CompletionQueue>(需确认RDMA库的QueuePair支持接受Arc包裹的CQ,或调整Client::new的参数适配):pub struct Client { qp: QueuePair, memory_manager: MemoryManager, cq: Arc<CompletionQueue>, // 用Arc持有CQ,确保生命周期同步 }修改异步函数:
创建Arc包裹的CompletionQueue,并传递给Client:pub async fn connect_to_clients( config: ClientConfig, ctx: Arc<Context>, pd: Arc<ProtectionDomain<'_>>, mut out: Vec<Client>, ) { info!("Running client mode."); let completion_queues: Vec<Arc<CompletionQueue>> = config .targets .iter() .enumerate() .map(|(i, _)| Arc::new(ctx.create_cq(1, i.try_into().unwrap()).unwrap())) .collect(); let mut i = 0; for c in config.targets { out.push(Client::new(&pd, &completion_queues[i], c.clone(), config.frame_size).await); i += 1; } }
备选方案
如果RDMA库的QueuePair必须使用裸引用,可将completion_queues与Client封装到同一个父结构体中,确保二者生命周期绑定。但Arc方案更灵活,适配异步场景下的所有权管理需求。
内容的提问来源于stack exchange,提问作者Joep Van Diessen

