FnOnce的实现在async fn中不够通用

8
以下代码(playground)无法编译:
async fn wrapper<F, Fut>(func: F)
where
    F: FnOnce(&i32) -> Fut,
    Fut: Future<Output = ()>,
{
    let i = 5;
    func(&i).await;
}

async fn myfunc(_: &i32) {}

fn main() {
    wrapper(myfunc);
}

错误信息为:
error: implementation of `std::ops::FnOnce` is not general enough
   --> src/main.rs:15:5
    |
15  |       wrapper(myfunc);
    |       ^^^^^^^ implementation of `std::ops::FnOnce` is not general enough
    |
    = note: `std::ops::FnOnce<(&'0 i32,)>` would have to be implemented for the type `for<'_> fn(&i32) -> impl std::future::Future {myfunc}`, for some specific lifetime `'0`...
    = note: ...but `std::ops::FnOnce<(&i32,)>` is actually implemented for the type `for<'_> fn(&i32) -> impl std::future::Future {myfunc}`

我找到了一些类似的内容,提到更高级别的特征边界 (例如 这个 Rust 问题这个问题),并尝试使用 for<'a> 和附加生命周期边界进行实验,但错误对我来说仍然不清楚。

我不明白为什么编译器无法确定生命周期,尤其是因为一个没有 futures 的简单版本可以编译通过:

fn myfunc(_: &i32) {}

fn wrapper<F: FnOnce(&i32)>(func: F) {
    let i = 5;
    func(&i);
}

fn main() {
    wrapper(myfunc);
}

这里实际上的问题是什么?是否有一种解决方法可以使其编译,例如增加边界等?
1个回答

2
从你提供的问题中所解释的内容开始,可以为您的包装器期望的函数创建一个特定的trait。
作为一个相对较新的Rust用户,我不知道为什么在您的原始版本中编译器会抱怨显示错误消息中实际上是等效的不匹配类型(当我们有明确的生命周期时,这更加明显;显示的类型完全匹配!)。 我只是猜想这是因为确切的结果类型被隐藏在一个impl符号后面,实际的区别在于这个隐藏部分。
确实很难表达任何生命周期for<'r>应该同时考虑&参数和Future结果,因为它们出现在where子句的两个不同约束条件中。 据我所理解(不太多...),考虑整个trait的一个明确生命周期使得for<'r>符号一致适用于&参数以及Future结果,因为所有这些都在where子句的同一约束条件中。
由于我们正在处理生命周期,我决定用不可复制的String替换可复制的i32,以防止发生任何意外的简化。
正如您提供的链接所述,这个解决方案似乎只适用于函数,而不是闭包。
use std::future::Future;

async fn wrapper<F>(func: F)
where
    F: for<'r> Wrapped<'r>,
{
    let s = String::from("WRAPPED");
    func.call_once(&s).await;
}

trait Wrapped<'a> {
    type Res: Future<Output = ()>;
    fn call_once(
        self,
        s: &'a String,
    ) -> Self::Res;
}

impl<'a, F, FutRes> Wrapped<'a> for F
where
    F: FnOnce(&'a String) -> FutRes,
    FutRes: Future<Output = ()> + 'a,
{
    type Res = FutRes;
    fn call_once(
        self,
        s: &'a String,
    ) -> Self::Res {
        self(s)
    }
}

async fn call_myfunc() {
    let s = String::from("HARDCODED");
    myfunc(&s).await;
}

async fn myfunc(arg: &String) {
    println!("arg={}", arg);
}

fn main() {
    println!("~~~~ hardcoded call ~~~~");
    let f = call_myfunc();
    futures::executor::block_on(f);
    println!("~~~~ use wrapper() ~~~~");
    let f = wrapper(myfunc);
    futures::executor::block_on(f);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接