假设我有一个如下所示的函数,它可能会失败.该函数也是异步的

async fn can_fail() -> Result<i32, Box<dyn std::error::Error>> {
    let mut rng = rand::thread_rng();
    let random: u8 = rng.gen();
    if random % 2u8 == 0 {
        Ok(42)
    } else {
       Err("error".to_string().into())
    }
}

现在我想实现一个retry函数,该函数可用于重试像can_fail这样的函数.

我在try 的时候想到了这一点

fn retry<F: Fn() -> Result<i32, Box<dyn std::error::Error>>>(f: F, retries: i32) -> Result<i32, Box<dyn std::error::Error>>
    {
    let mut count = 0;
    loop {
        let result = f();

        if result.is_ok() {
            break result;
        } else {
            if count > retries {
             break result
            }
            count += 1;
        }
    }
}

然后,在我try 使用时,我试图像这样将can_fail放入闭合

    let my_closure: Box<dyn Fn() -> Result<i32, Box<dyn std::error::Error>>> = Box::new(|| {
        can_fail().await
    });

但此操作失败,并显示错误

error[E0728]: `await` is only allowed inside `async` functions and blocks
   --> src/main.rs:208:19
    |
207 |     let my_closure: Box<dyn Fn() -> Result<i32, Box<dyn std::error::Error>>> = Box::new(|| {
    |                                                                                         -- this is not `async`
208 |         can_fail().await
    |                   ^^^^^^ only allowed inside `async` functions and blocks

所以我有点被困住了.所以我的问题是:

  1. 我想出的retry个能胜任这项工作吗?我不能说,因为我甚至不能结束它
  2. 如何修复此场景中的100 is only allowed inside 101 functions and blocks`错误?
  3. 还有,有没有可能让重试变得更通用?开始时,我对要返回的函数的返回类型进行了硬编码.在现实生活中,我希望它是通用的.如何才能做到这一点?

推荐答案

您的retry()函数看起来还可以,但要向其中传递一个异步函数,您需要修复它以接受返回Future的函数,并使其能够进行.await操作,使其成为异步函数:

async fn retry_async<Fut, F: Fn() -> Fut>(f: F, retries: i32) -> Result<i32, Box<dyn std::error::Error>>
where
    Fut: Future<Output = Result<i32, Box<dyn std::error::Error>>>,
{
    let mut count = 0;
    loop {
        let result = f().await;

        if result.is_ok() {
            break result;
        } else {
            if count > retries {
                break result;
            }
            count += 1;
        }
    }
}

retry(can_fail, 3).await.expect("failed to many times");

您还可以通过以下方式使该函数更加通用:

  • FnMut英镑而不是Fn英镑.
  • 允许泛型返回类型.
  • 允许泛型错误类型.
async fn retry_async<T, E, Fut, F: FnMut() -> Fut>(mut f: F, retries: i32) -> Result<T, E>
where
    Fut: Future<Output = Result<T, E>>,
{
    let mut count = 0;
    loop {
        let result = f().await;

        if result.is_ok() {
            break result;
        } else {
            if count > retries {
                break result;
            }
            count += 1;
        }
    }
}

Rust相关问答推荐

如何装箱生命周期相关联的两个对象?

MacOS(AARCH64)上Ghidra中的二进制补丁导致进程终止

两个相关特征的冲突实现错误

在使用#[NO_STD]时,如何在Rust中收到紧急消息?

无法定义名为&new&的关联函数,该函数的第一个参数不是self

不能在一个代码分支中具有不变的自身borrow ,而在另一个代码分支中具有可变的self borrow

获取与父字符串相关的&;str的原始片段

更合理的方法来设计样条线函数器?

如何强制匹配的返回类型为()?

提取指向特征函数的原始指针

仅发布工作区的二进制 crate

为什么某些类型参数仅在特征边界中使用的代码在没有 PhantomData 的情况下进行编译?

Rust Option 的空显式泛型参数

try 从标准输入获取用户名和密码并删除 \r\n

如何在 Rust 中按 char 对字符串向量进行排序?

从 HashMap>, _> 中删除的生命周期问题

BigUint 二进制补码

HashMap entry() 方法使borrow 的时间比预期的长

返回 &str 但不是 String 时,borrow 时间比预期长

来自外部函数的future 内部可变引用