< 返回版块

Nekobaex 发表于 2023-10-12 21:08

Tags:闭包,actix,Fn

在使用 actix_web 的时候发现的问题,
这是官方的代码:

use actix_web::{web, App, HttpServer, Responder};

async fn index() -> impl Responder {
    "Hello world!"
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new().service(
            // prefixes all resources and routes attached to it...
            web::scope("/app")
                // ...so this handles requests for `GET /app/index.html`
                .route("/index.html", web::get().to(index)),
        )
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

我觉得缩进层次有点深, 就想重构一下, 改为下面的样子:
(目前还没有问题)

use actix_web::{web, App, HttpServer, Responder};

async fn index() -> impl Responder {
    "Hello world!"
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {

    let app = || {
        App::new()
        .service(
            web::scope("/app")
            .route(
                "/index.html", 
                web::get().to(index)
            )
        )
    };

    HttpServer::new(app)
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

再次改进, 就出问题了:

use actix_web::{web, App, HttpServer, Responder};

async fn index() -> impl Responder {
    "Hello world!"
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let router = web::scope("/app").route(
        "/index.html", 
        web::get().to(index)
    );

    let app = || {
        App::new()
        .service(router)
    };

    HttpServer::new(app)
    .bind(("127.0.0.1", 8080))?
    .run()
    .await
}

报错特别长, 我仔细看了一下,
主要原因是将 router 放到外面后,
app 的类型就从 Fn 变为了 FnOnce,
而 HttpServer::new() 只接受 Fn, 所以就出问题了,
但不知道怎么解决,
报错内容:

error[E0525]: expected a closure that implements the `Fn` trait, but this 
closure only implements `FnOnce`
   --> src\main.rs:15:15
    |
15  |     let app = || {
    |               ^^ this closure implements `FnOnce`, not `Fn`
16  |         App::new()
17  |         .service(router)
    |                  ------ closure is `FnOnce` because it moves the variable `router` out of its environment
...
20  |     HttpServer::new(app)
    |     --------------- --- the requirement to implement `Fn` derives from here
    |     |
    |     required by a bound introduced by this call
    |
note: required by a bound in `HttpServer::<F, I, S, B>::new`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\server.rs:89:8
    |
89  |     F: Fn() -> I + Send + Clone + 'static,
    |        ^^^^^^^^^ required by this bound in `HttpServer::<F, I, S, B>::new`
...
102 |     pub fn new(factory: F) -> Self {
    |            --- required by a bound in this associated function      

error[E0277]: the trait bound `actix_web::Scope: Clone` is not satisfied in `{closure@src\main.rs:15:15: 15:17}`
   --> src\main.rs:20:21
    |
15  |     let app = || {
    |               -- within this `{closure@src\main.rs:15:15: 15:17}`   
...
20  |     HttpServer::new(app)
    |     --------------- ^^^ within `{closure@src\main.rs:15:15: 15:17}`, the trait `Clone` is not implemented for `actix_web::Scope`
    |     |
    |     required by a bound introduced by this call
    |
note: required because it's used within this closure
   --> src\main.rs:15:15
    |
15  |     let app = || {
    |               ^^
note: required by a bound in `HttpServer::<F, I, S, B>::new`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\server.rs:89:27
    |
89  |     F: Fn() -> I + Send + Clone + 'static,
    |                           ^^^^^ required by this bound in `HttpServer::<F, I, S, B>::new`
...
102 |     pub fn new(factory: F) -> Self {
    |            --- required by a bound in this associated function      

error[E0277]: `Rc<RefCell<Option<actix_web::scope::ScopeFactory>>>` cannot be sent between threads safely
   --> src\main.rs:20:21
    |
15  |     let app = || {
    |               -- within this `{closure@src\main.rs:15:15: 15:17}`   
...
20  |     HttpServer::new(app)
    |     --------------- ^^^ `Rc<RefCell<Option<actix_web::scope::ScopeFactory>>>` cannot be sent between threads safely
    |     |
    |     required by a bound introduced by this call
    |
    = help: within `{closure@src\main.rs:15:15: 15:17}`, the trait `Send` 
is not implemented for `Rc<RefCell<Option<actix_web::scope::ScopeFactory>>>`
note: required because it appears within the type `Scope`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\scope.rs:57:12
    |
57  | pub struct Scope<T = ScopeEndpoint> {
    |            ^^^^^
note: required because it's used within this closure
   --> src\main.rs:15:15
    |
15  |     let app = || {
    |               ^^
note: required by a bound in `HttpServer::<F, I, S, B>::new`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\server.rs:89:20
    |
89  |     F: Fn() -> I + Send + Clone + 'static,
    |                    ^^^^ required by this bound in `HttpServer::<F, I, S, B>::new`
...
102 |     pub fn new(factory: F) -> Self {
    |            --- required by a bound in this associated function      

error[E0277]: `Rc<actix_service::boxed::BoxServiceFactory<(), ServiceRequest, ServiceResponse, actix_web::Error, ()>>` cannot be sent between threads safely
   --> src\main.rs:20:21
    |
15  |     let app = || {
    |               -- within this `{closure@src\main.rs:15:15: 15:17}`   
...
20  |     HttpServer::new(app)
    |     --------------- ^^^ `Rc<actix_service::boxed::BoxServiceFactory<(), ServiceRequest, ServiceResponse, actix_web::Error, ()>>` cannot be sent between threads safely
    |     |
    |     required by a bound introduced by this call
    |
    = help: within `{closure@src\main.rs:15:15: 15:17}`, the trait `Send` 
is not implemented for `Rc<actix_service::boxed::BoxServiceFactory<(), ServiceRequest, ServiceResponse, actix_web::Error, ()>>`
note: required because it appears within the type `Option<Rc<BoxServiceFactory<(), ServiceRequest, ServiceResponse, Error, ()>>>`
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\option.rs:563:10
    |
563 | pub enum Option<T> {
    |          ^^^^^^
note: required because it appears within the type `Scope`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\scope.rs:57:12
    |
57  | pub struct Scope<T = ScopeEndpoint> {
    |            ^^^^^
note: required because it's used within this closure
   --> src\main.rs:15:15
    |
15  |     let app = || {
    |               ^^
note: required by a bound in `HttpServer::<F, I, S, B>::new`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\server.rs:89:20
    |
89  |     F: Fn() -> I + Send + Clone + 'static,
    |                    ^^^^ required by this bound in `HttpServer::<F, I, S, B>::new`
...
102 |     pub fn new(factory: F) -> Self {
    |            --- required by a bound in this associated function      

error[E0277]: `(dyn actix_web::service::AppServiceFactory + 'static)` cannot be sent between threads safely
   --> src\main.rs:20:21
    |
20  |     HttpServer::new(app)
    |     --------------- ^^^ `(dyn actix_web::service::AppServiceFactory 
+ 'static)` cannot be sent between threads safely
    |     |
    |     required by a bound introduced by this call
    |
    = help: the trait `Send` is not implemented for `(dyn actix_web::service::AppServiceFactory + 'static)`
    = note: required for `Unique<(dyn actix_web::service::AppServiceFactory + 'static)>` to implement `Send`
note: required because it appears within the type `Box<dyn AppServiceFactory>`
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\alloc\src\boxed.rs:195:12
    |
195 | pub struct Box<
    |            ^^^
    = note: required for `Unique<Box<(dyn actix_web::service::AppServiceFactory + 'static)>>` to implement `Send`
note: required because it appears within the type `RawVec<Box<dyn AppServiceFactory>>`
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\alloc\src\raw_vec.rs:51:19
    |
51  | pub(crate) struct RawVec<T, A: Allocator = Global> {
    |                   ^^^^^^
note: required because it appears within the type `Vec<Box<dyn AppServiceFactory>>`
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\alloc\src\vec\mod.rs:396:12
    |
396 | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = ... 
    |            ^^^
note: required because it appears within the type `Scope`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\scope.rs:57:12
    |
57  | pub struct Scope<T = ScopeEndpoint> {
    |            ^^^^^
note: required because it's used within this closure
   --> src\main.rs:15:15
    |
15  |     let app = || {
    |               ^^
note: required by a bound in `HttpServer::<F, I, S, B>::new`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\server.rs:89:20
    |
89  |     F: Fn() -> I + Send + Clone + 'static,
    |                    ^^^^ required by this bound in `HttpServer::<F, I, S, B>::new`
...
102 |     pub fn new(factory: F) -> Self {
    |            --- required by a bound in this associated function      

error[E0277]: `(dyn Guard + 'static)` cannot be sent between threads safely
   --> src\main.rs:20:21
    |
20  |     HttpServer::new(app)
    |     --------------- ^^^ `(dyn Guard + 'static)` cannot be sent between threads safely
    |     |
    |     required by a bound introduced by this call
    |
    = help: the trait `Send` is not implemented for `(dyn Guard + 'static)`
    = note: required for `Unique<(dyn Guard + 'static)>` to implement `Send`
note: required because it appears within the type `Box<dyn Guard>`        
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\alloc\src\boxed.rs:195:12
    |
195 | pub struct Box<
    |            ^^^
    = note: required for `Unique<Box<(dyn Guard + 'static)>>` to implement `Send`
note: required because it appears within the type `RawVec<Box<dyn Guard>>`   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\alloc\src\raw_vec.rs:51:19
    |
51  | pub(crate) struct RawVec<T, A: Allocator = Global> {
    |                   ^^^^^^
note: required because it appears within the type `Vec<Box<dyn Guard>>`   
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\alloc\src\vec\mod.rs:396:12
    |
396 | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = ... 
    |            ^^^
note: required because it appears within the type `Scope`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\scope.rs:57:12
    |
57  | pub struct Scope<T = ScopeEndpoint> {
    |            ^^^^^
note: required because it's used within this closure
   --> src\main.rs:15:15
    |
15  |     let app = || {
    |               ^^
note: required by a bound in `HttpServer::<F, I, S, B>::new`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\server.rs:89:20
    |
89  |     F: Fn() -> I + Send + Clone + 'static,
    |                    ^^^^ required by this bound in `HttpServer::<F, I, S, B>::new`
...
102 |     pub fn new(factory: F) -> Self {
    |            --- required by a bound in this associated function      

error[E0277]: `(dyn std::any::Any + 'static)` cannot be sent between threads safely
   --> src\main.rs:20:21
    |
20  |     HttpServer::new(app)
    |     --------------- ^^^ `(dyn std::any::Any + 'static)` cannot be sent between threads safely
    |     |
    |     required by a bound introduced by this call
    |
    = help: the trait `Send` is not implemented for `(dyn std::any::Any + 
'static)`
    = note: required for `Unique<(dyn std::any::Any + 'static)>` to implement `Send`
note: required because it appears within the type `Box<dyn Any>`
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\alloc\src\boxed.rs:195:12
    |
195 | pub struct Box<
    |            ^^^
    = note: required because it appears within the type `(TypeId, Box<dyn 
Any>)`
    = note: required for `hashbrown::raw::RawTable<(TypeId, Box<(dyn std::any::Any + 'static)>)>` to implement `Send`
note: required because it appears within the type `HashMap<TypeId, Box<dyn Any>, BuildHasherDefault<NoOpHasher>>`
   --> C:\Users\runneradmin\.cargo\registry\src\index.crates.io-6f17d22bba15001f\hashbrown-0.14.0\src\map.rs:188:12
note: required because it appears within the type `HashMap<TypeId, Box<dyn Any>, BuildHasherDefault<NoOpHasher>>`
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\std\src\collections\hash\map.rs:216:12
    |
216 | pub struct HashMap<K, V, S = RandomState> {
    |            ^^^^^^^
note: required because it appears within the type `Extensions`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-http-3.4.0\src\extensions.rs:33:12
    |
33  | pub struct Extensions {
    |            ^^^^^^^^^^
note: required because it appears within the type `Option<Extensions>`    
   --> C:\Users\baex\.rustup\toolchains\nightly-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\option.rs:563:10
    |
563 | pub enum Option<T> {
    |          ^^^^^^
note: required because it appears within the type `Scope`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\scope.rs:57:12
    |
57  | pub struct Scope<T = ScopeEndpoint> {
    |            ^^^^^
note: required because it's used within this closure
   --> src\main.rs:15:15
    |
15  |     let app = || {
    |               ^^
note: required by a bound in `HttpServer::<F, I, S, B>::new`
   --> C:\Users\baex\.cargo\registry\src\mirrors.ustc.edu.cn-12df342d903acd47\actix-web-4.4.0\src\server.rs:89:20
    |
89  |     F: Fn() -> I + Send + Clone + 'static,
    |                    ^^^^ required by this bound in `HttpServer::<F, I, S, B>::new`
...
102 |     pub fn new(factory: F) -> Self {
    |            --- required by a bound in this associated function      

Some errors have detailed explanations: E0277, E0525.
For more information about an error, try `rustc --explain E0277`.

评论区

写评论
潜水的猫 2023-10-14 02:23
let router = web::scope("/app").route(
        "/index.html", 
        web::get().to(index)
    );

    let app = App::new()
        .service(router);

    HttpServer::new(|| {
     app
    })
    .bind(("127.0.0.1", 8080))?
    .run()
    .await

换个写法吧

cutepig123 2023-10-14 01:22

把router移动到app闭包内行不行?



    let app = || {

let router = web::scope("/app").route(
        "/index.html", 
        web::get().to(index)
    );

        App::new()
        .service(router)
    };

--
👇
xiaopengli89: 看了下,HttpServer::new 的闭包设计上是 factory,需要能重复多次执行,但是 App::service 需要 router 的所有权,除非 router 实现了 clone,用 App:new().service(router.clone()),否则 Router 必须再 factory 内重新创建,不能复用

xiaopengli89 2023-10-12 21:36

看了下,HttpServer::new 的闭包设计上是 factory,需要能重复多次执行,但是 App::service 需要 router 的所有权,除非 router 实现了 clone,用 App:new().service(router.clone()),否则 Router 必须再 factory 内重新创建,不能复用

作者 Nekobaex 2023-10-12 21:22

又报错了: (截取最前面的一条)

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce`
   --> src\main.rs:15:19
    |
15  |         let app = move || { App::new() .service(router) };
    |                   ^^^^^^^                       ------ closure is `FnOnce` because it moves the variable `router` out of its environment
    |                   |
    |                   this closure implements `FnOnce`, not `Fn`
16  |
17  |     HttpServer::new(app)
    |     --------------- --- the requirement to implement `Fn` derives from here
    |     |
    |     required by a bound introduced by this call
    |

--  

👇
xiaopengli89: let app = move || { App::new() .service(router) };

xiaopengli89 2023-10-12 21:19

let app = move || { App::new() .service(router) };

1 共 5 条评论, 1 页