当您将
'a
作为通用参数指定时,意味着“我允许调用者选择任何它想要的生命周期”。调用者也可以选择
'static
。然后,您承诺传递
&'a mut i32
,即
&'static mut i32
。但是,
i
并没有存在于
'static
生命周期中!这就是第一个错误的原因。
第二个错误是因为您承诺您将可变地借用
i
作为
'a
。但同样,
'a
也可以覆盖整个函数,即使在丢弃结果后仍然如此!调用者也可以选择
'static
,然后将引用存储在全局变量中。如果在之后使用
i
,则会在其被可变借用时使用。炸裂!
您想要的不是让调用者选择生命周期,而是说“我正在传递带有一些生命周期
'a
的引用,并希望您以相同的生命周期返回未来”。我们用于实现“我正在给您一些生命周期,但让我选择”的效果称为
HRTB(Higher-Kinded Trait Bounds)。
如果您只想返回特定类型而不是通用类型,则应如下所示:
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;
}
游乐场。
实际上,您甚至可以摆脱显式的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;
}
游乐场。
更多信息:
'static
的那一刻我就知道我的推理出了问题,再一次忘记了for<'a>
,但是你的答案非常详细和有帮助! - KillianDSFnOnce(&'a mut Arg)
来使特质适用于闭包,其中Arg
为'static
,但我遇到了这个Rust问题。因此,目前似乎存在语言限制。 - KillianDS