如何存储和使用接受引用并返回未来的可选闭包?

Posted

技术标签:

【中文标题】如何存储和使用接受引用并返回未来的可选闭包?【英文标题】:How to store and use an optional closure that accepts a reference and returns a future? 【发布时间】:2020-10-27 14:26:49 【问题描述】:

我有一个异步函数 save,它有一个 Save 结构作为参数,它可以选择包含一个异步函数 (validator)。问题是以下代码仅在指定Some(..) 时有效,而None 编译器会抛出错误。

use std::future::Future;

trait SomeTrait 

enum SomeError 

#[derive(Debug)]
struct User 

impl SomeTrait for User 

struct Save<T, F>
where
    T: SomeTrait,
    F: Future<Output = Result<(), SomeError>>,

    pub validator: Option<Box<dyn Fn(&T) -> F>>,


async fn save<T, F>(obj: &T, args: Save<T, F>) -> Result<(), SomeError>
where
    T: SomeTrait,
    F: Future<Output = Result<(), SomeError>>,

    if let Some(v) = args.validator 
        (*v)(obj).await?;
    
    Ok(())


#[tokio::test]
async fn test_func() 
    let user = User ;

    save(&user, Save  validator: None ).await;
    save(
        &user,
        Save 
            validator: Some(Box::new(|obj| async 
                println!("input: :?", obj);
                Ok(())
            )),
        ,
    )
    .await;

错误:

error[E0698]: type inside `async` block must be known in this context
  --> test_utils/src/testin.rs:35:17
   |
35 |     save(&user, Save  validator: None ).await;
   |                 ^^^^ cannot infer type for type parameter `F` declared on the struct `Save`
   |
note: the type is part of the `async` block because of this `await`
  --> test_utils/src/testin.rs:35:5
   |
35 |     save(&user, Save  validator: None ).await;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

我怎样才能使上面的代码工作?在Save 结构中是否有不使用F 泛型参数的替代实现?我现在可以使用它,但是当Save 结构中有多个函数时可能会变得笨拙。

【问题讨论】:

"异步闭包不稳定" — 如果您使用不稳定的功能,您必须注意您使用的是哪个夜间版本。如果您没有使用夜间编译器,那么您就没有使用异步闭包。请edit澄清您的问题。 另见What is the difference between |_| async move and async move |_| ; What's the difference of lifetime inference between async fn and async closure? 另见How to accept an async function as an argument?; How to indicate that the lifetime of an async function's return value is the same as a parameter?. 感谢您的反馈!我正在阅读您现在引用的页面,并将很快更新我的问题 我认为我的情况因使用了Option而有所不同,只有在使用None时才会出现问题。 【参考方案1】:

使用BoxFuture

由于您想隐藏类型,因此使用 trait 对象很有用。 BoxFuture 非常适合这个,结合boxed 方法来创建它:

use futures::future::BoxFuture, FutureExt;
struct Save<T>
where
    T: SomeTrait,

    pub validator: Option<Box<dyn Fn(&T) -> BoxFuture<Result<(), SomeError>>>>,

let _ = save(
    &user,
    Save 
        validator: Some(Box::new(|obj| 
            async move 
                println!("input: :?", obj);
                Ok(())
            
            .boxed()
        )),
    ,
)
.await;

另见:

How can one await a result of a boxed future? Cannot use `impl Future` to store async function in a vector How do I erase the type of future in the new future API?

对泛型类型使用 None

这里的问题是必须知道泛型类型,即使您没有使用它,因为您选择了None。您可以提供适合约束的类型(实现 FutureOutputResult&lt;(), SomeError&gt;)。这里我使用Ready

type Dummy = futures::future::Ready<Result<(), SomeError>>;

save::<_, Dummy>(&user, Save  validator: None ).await;

不幸的是,这会产生一个我还不知道如何解决的错误(“借来的数据不能存储在它的闭包之外”)。

另见:

Create a generic struct with Option<T> without specifying T when instantiating with None Pass None into a function that accepts Option Is there a way to hint to the compiler to use some kind of default generic type when using Option::None?

【讨论】:

以上是关于如何存储和使用接受引用并返回未来的可选闭包?的主要内容,如果未能解决你的问题,请参考以下文章

C++ - 通过引用的可选参数

SwiftUI - 我们如何重新绑定绑定的可选参数?

python argparse - 我可以只使用互斥的可选参数还是有更好的方法

可选类型的值必须被解包以引用成员,但它是隐式解包的可选属性

如何键入提示函数的可选返回参数? [复制]

实体框架核心 - 无法删除可选的可为空关系