【问题标题】:Why does capturing an Arc by move make my closure FnOnce not Fn为什么通过移动捕获弧使我的关闭 FnOnce 不是 Fn
【发布时间】:2020-06-15 07:20:45
【问题描述】:

在下面的示例中,我使用 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】:

    嗯,异步闭包是不稳定的,所以这可能是一个错误。我认为当异步块捕获Arc 时,它会消耗它,因此实际上只能调用一次闭包。我的意思是,异步闭包是某种生成器:每次调用它时,它都会构造一个未来,而未来会保留捕获的值。

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

    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 足以确定。

    【讨论】:

    • “我认为实际上 Warp::Filter 应该接受一个 FnOnce 开始,但我对 warp 的了解不足以确定。” – 每个匹配的请求都会调用过滤器,所以据我了解,我认为FnOnce 还不够。
    • @devnev:啊,因为你当然要构建一个服务器!我看到post 并假设是客户端代码。
    猜你喜欢
    • 2022-09-24
    • 2019-04-13
    • 2015-07-22
    • 2021-04-07
    • 2014-12-07
    • 1970-01-01
    • 1970-01-01
    • 2012-11-11
    相关资源
    最近更新 更多