期望一个实现“Fn”特征的闭包,但这个闭包只在 actix web 中实现“FnOnce”

expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` in actix web

我正在使用 actix web,我正在尝试 return 闭包中的异步函数,但出现以下错误:



error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
   --> src/server.rs:134:33
    |
133 |    ...                   web::get().to(
    |                                     -- the requirement to implement `Fn` derives from here
134 |  / ...                       move |router: web::Data<Arc<Router>>,
135 |  | ...                             headers: web::Data<Arc<Headers>>,
136 |  | ...                             stream: web::Payload,
137 |  | ...                             req: HttpRequest| async {
    |  |_________________________________________________________-
138 | || ...                           start_web_socket(req, stream, params).await
139 | || ...                       },
    | ||                           ^
    | ||___________________________|
    | |____________________________this closure implements `FnOnce`, not `Fn`
    |                              closure is `FnOnce` because it moves the variable `params` out of its environment


error: aborting due to previous error; 1 warning emitted

这是导致错误的代码段。我已尝试尽可能多地记录代码。

我试过将变量移入和移出移动块,并尝试将它们放置在不同的地方,但没有成功。

应该怎么做而不是这样做?

    pub fn start(
        &mut self,
        py: Python,
        url: String,
        port: u16,
        socket: &PyCell<SocketHeld>,
        name: String,
        workers: usize,
    ) -> PyResult<()> {
        if STARTED
            .compare_exchange(false, true, SeqCst, Relaxed)
            .is_err()
        {
            println!("Already running...");
            return Ok(());
        }

        println!("{}", name);

        let borrow = socket.try_borrow_mut()?;
        let held_socket: &SocketHeld = &*borrow;

        let raw_socket = held_socket.get_socket();
        println!("Got our socket {:?}", raw_socket);

        let router = self.router.clone();
        let headers = self.headers.clone();
        let directories = self.directories.clone();
        let workers = Arc::new(workers);

        let asyncio = py.import("asyncio").unwrap();

        let event_loop = asyncio.call_method0("new_event_loop").unwrap();
        asyncio
            .call_method1("set_event_loop", (event_loop,))
            .unwrap();
        let event_loop_hdl = PyObject::from(event_loop);

        thread::spawn(move || {
            //init_current_thread_once();
            actix_web::rt::System::new().block_on(async move {
                let addr = format!("{}:{}", url, port);

                println!("The number of workers are {}", workers.clone());

                HttpServer::new(move || {
                    let mut app = App::new();
                    let event_loop_hdl = event_loop_hdl.clone();
                    let directories = directories.read().unwrap();
                    let router_copy = router.clone();

                    // this loop matches three types of directory serving
                    // 1. Serves a build folder. e.g. the build folder generated from yarn build
                    // 2. Shows file listing
                    // 3. Just serves the file without any redirection to sub links
                    for directory in directories.iter() {
                        if let Some(index_file) = &directory.index_file {
                            app = app.service(
                                Files::new(&directory.route, &directory.directory_path)
                                    .index_file(index_file)
                                    .redirect_to_slash_directory(),
                            );
                        } else if directory.show_files_listing {
                            app = app.service(
                                Files::new(&directory.route, &directory.directory_path)
                                    .redirect_to_slash_directory()
                                    .show_files_listing(),
                            );
                        } else {
                            app = app
                                .service(Files::new(&directory.route, &directory.directory_path));
                        }
                    }

                    app = app
                        .app_data(web::Data::new(router.clone()))
                        .app_data(web::Data::new(headers.clone()));

                    let web_socket_map = router_copy.get_web_socket_map().unwrap();
                    for elem in (web_socket_map).iter() {
                        let route = elem.key().clone();
                        let params = elem.value().clone();
                        app = app.route(
                            &route,
                            web::get().to(
                                move |router: web::Data<Arc<Router>>,
                                      headers: web::Data<Arc<Headers>>,
                                      stream: web::Payload,
                                      req: HttpRequest| async {
                                    start_web_socket(req, stream, params).await
                                },
                            ),
                        )
                    }

                    app.default_service(web::route().to(move |router, headers, payload, req| {
                        pyo3_asyncio::tokio::scope_local(event_loop_hdl.clone(), async move {
                            index(router, headers, payload, req).await
                        })
                    }))
                })
                .keep_alive(KeepAlive::Os)
                .workers(*workers.clone())
                .client_timeout(0)
                .listen(raw_socket.try_into().unwrap())
                .unwrap()
                .run()
                .await
                .unwrap();
            });
        });

----更新----

感谢您的建议。在更新 params 时,我能够摆脱原来的错误,但我得到了一个类似但新的错误:

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
   --> src/server.rs:134:33
    |
133 |    ...                   web::get().to(
    |                                     -- the requirement to implement `Fn` derives from here
134 |  / ...                       |router: web::Data<Arc<Router>>,
135 |  | ...                        headers: web::Data<Arc<Headers>>,
136 |  | ...                        stream: web::Payload,
137 |  | ...                        req: HttpRequest| async move {
    |  |_________________________________________________________-
138 | || ...                           start_web_socket(req, stream, params.clone()).await
139 | || ...                       },
    | ||                           ^
    | ||___________________________|
    | |____________________________this closure implements `FnOnce`, not `Fn`
    |                              closure is `FnOnce` because it moves the variable `params` out of its environment

新代码段如下所示:

                    let web_socket_map = router_copy.get_web_socket_map().unwrap();
                    for elem in (web_socket_map).iter() {
                        let route = elem.key().clone();
                        let params = elem.value().clone();
                        app = app.route(
                            &route,
                            web::get().to(
                                |router: web::Data<Arc<Router>>,
                                 headers: web::Data<Arc<Headers>>,
                                 stream: web::Payload,
                                 req: HttpRequest| async move {
                                    start_web_socket(req, stream, params.clone()).await
                                },
                            ),
                        )
                    }

我已经尝试在 async move 中克隆参数,但它仍然给出相同的错误。

web::get() returns 一个 Route, and web::get().to(...) is a Route method that expects a Handler。 Handler 应该是一个异步函数 (async fn) - 调用时 returns 一个 Future 的函数。

问题是在你的代码中你传递了一个异步块,这是一个未来。

An async block is a variant of a block expression which evaluates to a future.

所以你的代码:

async move |...| { // Future
    start_web_socket(req, stream, params.clone()).await
}

可以转换为:

move |...| { // Handler Fn
    async move { // Future
        start_web_socket(req, stream, params.clone()).await
    }
}

这相当于:

move |...| { // Handler Fn
    start_web_socket(req, stream, params.clone()) // Future
}

因为当你在没有 .await 的情况下调用 async fn start_web_socket 时,你会得到一个 Future。

调试此类事物的一个技巧是将事物分配给中间变量,并检查编译器为它们推断的类型。