rust 定义调用异步闭包的函数?

时间:2021-05-06 09:00:03

标签: kotlin rust async-await

假设我们有一些重复的异步代码来调用 htttp, 现在我们要编写一个函数,该函数将采用与闭包相同的代码并记录请求之前和请求之后的所有内容。

这是我想在 kotlin 中实现的简化代码(Rust 专家只需查看下面的 Rust 代码忽略 kotlin 示例)

suspend fun <A, B> logRequest(req: A, callable: suspend (A) -> B): B {
   println("write down the request $req")
   val rep: B = callable(req)
   println("write down the response $rep")
   return rep
}

这是一个简单的函数,它接受类型为 A 的请求和一个闭包,该闭包将采用相同的 A 执行一些异步逻辑并返回一些 B...注意闭包可挂起(异步)

这会像这样使用

   val request = "REQUEST"
   val response = logRequest(request) {
      println("my closure uses '$it' and can do some async stuff")
      delay(1.second) // that is async
      "RESPONSE"
   }

如果你运行,你会得到输出

write down the request REQUEST
my closure uses 'REQUEST' and can do some async stuff
write down the response RESPONSE

现在尝试用 rust 做同样的事情(我是新手)

use std::fmt::Display;

// ignore this
async fn delay(millis: i64) {}

fn log_request<A: Display, B: Display>(req: A, callable: impl FnOnce(A) -> B) -> B {
    println!("write down the request {}", &req);
    let rep: B = callable(req);
    println!("write down the response {}", &rep);
    rep
}

#[tokio::main]
async fn main() {
    let request = "REQUEST";

    let callable = |it: &str| -> &str {
        println!("my closure uses '{}' and can do some async stuff", it);
        // delay(1000).await; // uncommenting this
        "RESPONSE"
    };

    let response = log_request(request, callable);
}

在 Rust 中执行此操作的正确方法是什么?

如果取消注释延迟调用编译器会说 |it: &str| -> &str { is not async 那么如何将其定义为异步... 因为我已经在异步范围内运行,所以我肯定能够调用异步闭包(相当于 kotlin 中的可挂起函数)

1 个答案:

答案 0 :(得分:2)

Kotlin 和 Rust 示例之间的区别在于,您的 Kotlin log_requestsuspend,我认为这与 Rust 中的 async fn 相同。您也可以通过在 Rust log_request 中制作 async 来获得您想要的东西。这需要一些额外的步骤来等待 Future 中的 log_request 并对函数定义进行一些调整。

async 上添加 log_request 后,您需要将闭包的输出更改为返回 Future<Output=C> where C: Display 的内容。然后你需要await闭包的输出才能真正得到可显示的值。

另一个变化是在闭包定义本身内,因为闭包不是 async,你需要通过从它返回一个 Future 来解决这个问题。使用 async {} 块是这里的解决方案。

这些步骤使您的某些类型归属无效,例如闭包上的 &str 属性可以完全删除,如果您想保留 rep: B 注释,则需要将其更改为 rep: C

use std::fmt::Display;
use std::future::Future;

// ignore this
async fn delay(millis: i64) {}

async fn log_request<A: Display, B, C: Display>(req: A, callable: impl FnOnce(A) -> B) -> C
where
    B: Future<Output = C>,
{
    println!("write down the request {}", &req);
    let rep: C = callable(req).await;
    println!("write down the response {}", &rep);
    rep
}

#[tokio::main]
async fn main() {
    let request = "REQUEST";

    let callable = |it| {
        async move {
            println!("my closure uses '{}' and can do some async stuff", it);
            delay(1000).await; // uncommenting this
            "RESPONSE"
        }
    };

    let response = log_request(request, callable).await;
}

Playground Link