为什么通过移动捕获弧使我的闭合FnOnce不是Fn

问题描述 投票:2回答:1

在下面的示例中,我使用Arc从请求处理程序中引用服务器状态,但是编译器将闭包设置为FnOnce。感觉我在做正确的事情,因为每个闭包对状态都有很强的引用。为什么不起作用?有哪些选择可以使其正常工作?其他问题,例如Share Arc between closures表示类似的工作方式,但是我正在按显示进行每个封闭的克隆,但仍然出现错误。

#![feature(async_closure)]

#[derive(Default, Debug)]
struct State {}

impl State {
    pub async fn exists(&self, key: &str) -> bool {
        true
    }

    pub async fn update(&self, key: &str) {}
}

#[tokio::main]
async fn main() {
    use warp::Filter;
    use std::sync::Arc;

    let state: Arc<State> = Arc::default();

    let api = warp::post()
        .and(warp::path("/api"))
        .and(warp::path::param::<String>().and_then({
            let state = Arc::clone(&state);
            async move |p: String| {
                let x = state.exists(&p);
                if x.await {
                    Ok(p)
                } else {
                    Err(warp::reject::not_found())
                }
            }
        }))
        .and_then({
            let state = Arc::clone(&state);
            async move |id: String| {
                state.update(&id).await;
                Result::<String, warp::Rejection>::Ok("".to_owned())
            }
        });

    warp::serve(api).run(([127, 0, 0, 1], 0)).await;
}
error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
  --> src/main.rs:25:13
   |
23 |           .and(warp::path::param::<String>().and_then({
   |                                              -------- the requirement to implement `Fn` derives from here
24 |               let state = Arc::clone(&state);
25 |               async move |p: String| {
   |  _____________^^^^^^^^^^^^^^^^^^^^^^_-
   | |             |
   | |             this closure implements `FnOnce`, not `Fn`
26 | |                 let x = state.exists(&p);
27 | |                 if x.await {
28 | |                     Ok(p)
...  |
31 | |                 }
32 | |             }
   | |_____________- closure is `FnOnce` because it moves the variable `state` out of its environment
rust closures
1个回答
1
投票

嗯,异步闭包是不稳定的,因此可能是一个错误。我认为,当异步块捕获Arc时,它将使用它,因此该闭包实际上只能被调用一次。我的意思是,异步闭包是某种生成器:每次调用它都会构建一个future,并且future会保留捕获的值。

作为解决方法,您可以编写如下内容:

let state = Arc::clone(&state);
move |p: String| {
    let state = Arc::clone(&state);
    async move {
        let x = state.exists(&p);
        //...
    }
}

在闭包内部但在async块之前进行另一个克隆,可确保您可以根据需要多次调用闭包。

[我认为实际上Warp::Filter应该以FnOnce开头,但我对warp的了解不够确定。

© www.soinside.com 2019 - 2024. All rights reserved.