当您将 'a 指定为通用参数时,您的意思是“我允许调用者选择它想要的任何生命周期”。例如,来电者也可以选择'static。然后你承诺传&'a mut i32,即&'static mut i32。但是i并不为'static而活!这就是第一个错误的原因。
第二个错误是因为你承诺你为'a可变地借用i。但同样,'a 也可能涵盖整个功能,即使在您丢弃结果之后也是如此!例如,调用者可以选择'static,然后将引用存储在全局变量中。如果您之后使用 i,则在可变借用时使用它。繁荣!
你想要的是不是让来电者选择生命周期,而不是说“我正在向你传递一个参考一些lifetime 'a,我要你还我一个有相同生命的未来”。我们用什么来实现“我给你一些生命,但让我选择”的效果是HRTB(更高种类的特征界限)。
如果您只想返回特定类型,而不是泛型类型,它看起来像:
async fn call_changer<'a, F, Fut>(changer: F)
where
F: for<'a> FnOnce(&'a mut i32) -> &'a mut i32,
{ ... }
您也可以通过以下语法使用 Box<dyn Future>:
use std::future::Future;
use std::pin::Pin;
async fn call_changer<F>(changer: F)
where
F: for<'a> FnOnce(&'a mut i32) -> Pin<Box<dyn Future<Output = ()> + 'a>>,
{
let mut i = 0;
changer(&mut i).await;
dbg!(i);
}
#[tokio::main]
async fn main() {
call_changer(|i| {
Box::pin(async move {
*i = 100;
})
})
.await;
}
Playground。
事实上,您甚至可以去掉显式的 for 子句,因为 HRTB 是闭包中生命周期的默认脱糖:
where
F: FnOnce(&mut i32) -> &mut i32,
where
F: FnOnce(&mut i32) -> Pin<Box<dyn Future<Output = ()> + '_>>,
剩下的唯一问题是:我们如何用通用的 Fut 来表达这个?
尝试将 for<'a> 应用于多个条件很诱人:
where
for<'a>
F: FnOnce(&'a mut i32) -> Fut,
Fut: Future<Output = ()> + 'a,
或者:
where
for<'a> FnOnce(&'a mut i32) -> (Fut + 'a),
Fut: Future<Output = ()>,
但不幸的是,两者都不起作用。
我们可以做什么?
一种选择是留在Pin<Box<dyn Future>>。
另一种是使用自定义特征:
trait AsyncSingleArgFnOnce<Arg>: FnOnce(Arg) -> <Self as AsyncSingleArgFnOnce<Arg>>::Fut {
type Fut: Future<Output = <Self as AsyncSingleArgFnOnce<Arg>>::Output>;
type Output;
}
impl<Arg, F, Fut> AsyncSingleArgFnOnce<Arg> for F
where
F: FnOnce(Arg) -> Fut,
Fut: Future,
{
type Fut = Fut;
type Output = Fut::Output;
}
async fn call_changer<F>(changer: F)
where
F: for<'a> AsyncSingleArgFnOnce<&'a mut i32, Output = ()>,
{
let mut i = 0;
changer(&mut i).await;
dbg!(i);
}
不幸的是,这不适用于闭包。我不知道为什么。你必须输入fn:
#[tokio::main]
async fn main() {
async fn callback(i: &mut i32) {
*i += 100;
}
call_changer(callback).await;
}
Playground。
想要查询更多的信息: