你真正需要的是这样的:
pub async fn run_transaction<T, E, F, Fut>(f: F) -> Result<T, TransactionError<E>>
where
for<'a>
F: Fn(&'a mut ClientSession) -> Fut,
Fut: Future<Output = Result<T, TransactionError<E>>> + 'a {
不幸的是,这不起作用,因为中定义的“较高等级特征界限”(HRTB)for<'a>
只适用于下一个界限,而不适用于每一个界限,并且没有办法连接两个生命周期......
但并不是一切都失去了!我找到了这个question在 Rust 支持论坛中遇到类似问题,可以根据您的问题进行调整。基本思想是你创建一个特征来包装Fn
和Future
具有相同生命周期的界限:
pub trait XFn<'a, I: 'a, O> {
type Output: Future<Output = O> + 'a;
fn call(&self, session: I) -> Self::Output;
}
impl<'a, I: 'a, O, F, Fut> XFn<'a, I, O> for F
where
F: Fn(I) -> Fut,
Fut: Future<Output = O> + 'a,
{
type Output = Fut;
fn call(&self, x: I) -> Fut {
self(x)
}
}
现在你的绑定函数很简单:
pub async fn run_transaction<T, E, F>(f: F) -> Result<T, TransactionError<E>>
where for<'a>
F: XFn<'a, &'a mut ClientSession, Result<T, TransactionError<E>>>
请记住,要调用您必须编写的函数f.call(&mut session)
.
不幸的是,电话run_transaction()
,因为它是,不编译,说实施FnOnce
不够通用。我认为这是一个限制/错误async move
因为异步闭包不稳定。但您可以使用适当的异步函数:
async fn do_the_thing(session: &mut ClientSession) -> Result<Document, TransactionError<Never>> {
let document = collection().find_one_with_session(None, None, session).await?.unwrap();
let r: Result<Document, TransactionError<Never>> = Ok(document);
return r;
}
run_transaction(do_the_thing).await;
如果您认为这太复杂,并且不介意支付非常小的运行时价格,那么还有一个更简单的选择:您可以将返回的 future 装箱,完全避免第二个泛型:
pub async fn run_transaction<T, E, F>(f: F) -> Result<T, TransactionError<E>>
where for<'a>
F: Fn(&'a mut ClientSession) -> Pin<Box<dyn Future<Output = Result<T, TransactionError<E>>> + 'a>>
然后,调用它:
run_transaction(|mut session| Box::pin(async move {
let document = collection().find_one_with_session(None, None, session).await?.unwrap();
let r: Result<Document, TransactionError<Never>> = Ok(document);
return r;
}));