Rust中async_trait与借用问题:`collection`生命周期报错求助
Rust async_trait 与借用生命周期问题排查
依赖项
[dependencies] mongodb = { version = "2.3.1"} tokio = "1.22.0" twilio-openapi = "1.0.0" async-trait = "0.1.58" futures = "0.3.25"
Repository Trait 定义
基于async_trait实现的数据访问仓库Trait:
use std::pin::Pin; use async_trait::async_trait; use futures::Future; use mongodb::{bson::Document, error::Error, options::FindOneOptions, Client}; pub mod notification_settings; #[async_trait] pub trait Repository<'a> { fn with_client(client: &'a mut &Client) -> Self; async fn find_one<'b>( self, filter: Document, find_one_options: FindOneOptions, ) -> Pin<Box<(dyn Future<Output = Result<Option<Document>, Error>> + Send + 'b)>>; }
对async_trait要求返回Pin<Box<dyn Future<Output = X> + Send + 'b>>的原因及替代方案存在疑问。
结构体实现
实现上述Trait的NotificationSettingsRepository:
use std::{marker::Send, pin::Pin}; use async_trait::async_trait; use futures::Future; use mongodb::{bson::Document, error::Error, options::FindOneOptions, Client}; pub struct NotificationSettingsRepository<'a> { pub(crate) client: &'a Client, } #[async_trait] impl<'a> super::Repository<'a> for NotificationSettingsRepository<'a> { fn with_client(client: &'a mut &Client) -> Self { NotificationSettingsRepository { client: client } } async fn find_one<'b>( self, filter: Document, find_one_options: FindOneOptions, ) -> Pin<Box<(dyn Future<Output = Result<Option<Document>, Error>> + Send + 'b)>> { let collection = self .client .database("Occasionally") .collection::<Document>("NotificationSettings"); let document = collection.find_one(filter, find_one_options); Box::pin(async { document.await }) } }
编译错误信息
在collection.find_one(filter, find_one_options);处触发报错:
`collection` does not live long enough borrowed value does not live long enoughrustcClick for full compiler diagnostic notification_settings.rs(30, 5): `collection` dropped here while still borrowed notification_settings.rs(17, 23): lifetime `'b` defined here notification_settings.rs(21, 10): type annotation requires that `collection` is borrowed for `'b`
已知问题源于find_one通过&self借用collection,但尝试用async {}包装等操作无效,寻求解决办法。
问题分析与解决方案
错误根源
- 局部变量
collection在函数执行完毕后会被销毁,但find_one返回的Future持有对它的引用,要求其生命周期至少覆盖'b,这一约束无法满足。 - 对
async_trait的使用存在误区:默认情况下async_trait会自动处理Future的生命周期,手动指定返回Pin<Box<dyn Future<...>>>反而引入了生命周期冲突。
修正方案
- 简化Trait定义,让
async_trait自动处理异步返回值:
#[async_trait] pub trait Repository<'a> { fn with_client(client: &'a Client) -> Self; async fn find_one( &self, filter: Document, find_one_options: Option<FindOneOptions>, ) -> Result<Option<Document>, Error>; }
- 修正结构体实现:
#[async_trait] impl<'a> super::Repository<'a> for NotificationSettingsRepository<'a> { fn with_client(client: &'a Client) -> Self { NotificationSettingsRepository { client } } async fn find_one( &self, filter: Document, find_one_options: Option<FindOneOptions>, ) -> Result<Option<Document>, Error> { let collection = self .client .database("Occasionally") .collection::<Document>("NotificationSettings"); collection.find_one(filter, find_one_options).await } }
关键调整点
- 移除Trait中
find_one手动指定的Future返回类型,交由async_trait自动处理,避免生命周期手动管理混乱。 - 将
find_one的参数从self改为&self,允许重复使用Repository实例,而非每次调用就消耗实例。 - 简化
with_client的参数类型,直接接受&'a Client,去掉冗余的双重引用&'a mut &Client。 - 将
FindOneOptions改为Option<FindOneOptions>,更符合MongoDB Rust驱动的使用习惯(允许不传参数)。
内容的提问来源于stack exchange,提问作者LukasDeco
相关产品推荐
相关产品推荐

