我的问题的最小例子。
use std::future::Future;
async fn call_changer<'a, F, Fut>(changer: F)
where
F: FnOnce(&'a mut i32) -> Fut,
Fut: Future<Output = ()> + 'a,
{
let mut i = 0;
changer(&mut i).await; // error 1
dbg!(i); // error 2
}
#[tokio::main]
async fn main() {
call_changer(|i| async move {
*i = 100;
})
.await;
}
Run Code Online (Sandbox Code Playgroud)
这会导致两个相关的错误,请参阅Rust Playground以获取详细输出:
i的末端。call_changeri不能在await之后使用,因为它仍然被可变地借用。我对两者都感到有点惊讶,我理解为什么Future返回需要与其借用F具有相同的生命周期( )(相关的异步书籍部分)。然而,根据同一个参考文献,一旦我调用了 wait on的结果,借用就应该结束,这显然不会发生,否则我就不会出现这些错误。将此示例重写为类似本书的内容,其中函数不作为参数传递,而是直接调用,按预期工作。'achangerchanger
这是怎么回事?我能做些什么吗?&mut用构造替换Rc<RefCell<_>>可以按预期工作,但如果可能的话我想避免这种情况。
Cha*_*man 22
当您指定'a为通用参数时,您的意思是“我允许调用者选择它想要的任何生命周期”。'static例如,调用者也可以选择。那么你就答应通过&'a mut i32,也就是&'static mut i32。但i不为之而活'static!这就是第一个错误的原因。
第二个错误是因为你承诺你会i可变地借钱'a。但同样,'a即使您丢弃了结果,也可能会覆盖整个函数!'static例如,调用者可以选择,然后将引用存储在全局变量中。如果你使用iafter,则您可以在可变借用时使用它。繁荣!
你想要的不是让调用者选择生命周期,而是说“我正在向你传递一个具有某个生命周期的参考'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,
{ ... }
Run Code Online (Sandbox Code Playgroud)
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;
}
Run Code Online (Sandbox Code Playgroud)
游乐场。
事实上,您甚至可以摆脱显式for子句,因为 HRTB 是闭包中生命周期的默认脱糖:
where
F: FnOnce(&mut i32) -> &mut i32,
Run Code Online (Sandbox Code Playgroud)
where
F: FnOnce(&mut i32) -> Pin<Box<dyn Future<Output = ()> + '_>>,
Run Code Online (Sandbox Code Playgroud)
剩下的唯一问题是:我们如何用 generic 来表达这一点Fut?
尝试将其应用于for<'a>多个条件是很诱人的:
where
for<'a>
F: FnOnce(&'a mut i32) -> Fut,
Fut: Future<Output = ()> + 'a,
Run Code Online (Sandbox Code Playgroud)
或者:
where
for<'a> FnOnce(&'a mut i32) -> (Fut + 'a),
Fut: Future<Output = ()>,
Run Code Online (Sandbox Code Playgroud)
但不幸的是,两者都不起作用。
我们可以做什么?
一种选择是留下来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);
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,这不适用于闭包。我不知道为什么。你必须放一个fn:
#[tokio::main]
async fn main() {
async fn callback(i: &mut i32) {
*i += 100;
}
call_changer(callback).await;
}
Run Code Online (Sandbox Code Playgroud)
游乐场。
了解更多信息:
| 归档时间: |
|
| 查看次数: |
1948 次 |
| 最近记录: |