Tengo el siguiente código:

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

use actix_router::{Path, Url};
use actix_web::dev::{ServiceRequest, ServiceResponse};
use actix_web::error::ResponseError;
use actix_web::{http, http::StatusCode, Error, HttpRequest, HttpResponse};

async fn greet(req: HttpRequest) -> impl Responder {
    let name = req.match_info().get("name").unwrap_or("World");
    format!("Hello {}!", &name)
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        let app = App::new()
            .wrap_fn(|req, srv| {
                let passed: bool;

                // change to false to simulate a failed check
                let check = true;

                if *&req.path().contains("/item/") {
                    passed = check;
                } else {
                    passed = true;
                }

                let fresh_result = match passed {
                    true => {
                        let fut = srv.call(req);
                        Box::pin(async {
                            let result = fut.await?;
                            Ok(result)
                        })
                    }
                    false => Box::pin(async {
                        let result = req.into_response(
                            HttpResponse::Found()
                                .header(http::header::LOCATION, "/login")
                                .finish()
                                .into_body(),
                        );
                        Ok(result)
                    }),
                };
                async {
                    let last_outcome = fresh_result.await?;
                    Ok(last_outcome)
                }
            })
            .route("/", web::get().to(greet));
        return app;
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

Sin embargo, obtengo el siguiente error:

110 |                         let fresh_result = match passed {
    |    ________________________________________-
111 |   |                         true => {
112 |   |                             let fut = srv.call(req);
113 |   |                             Box::pin(
    |  _|_____________________________-
114 | | |                                 async {
115 | | |                             let result = fut.await?;
116 | | |                             Ok(result)
117 | | |                             }
118 | | |                             )
    | |_|_____________________________- this is found to be of type `std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>`
...     |
121 | / |                             Box::pin(
122 | | |                             async {
123 | | |                             let result = req.into_response(
124 | | |                                 HttpResponse::Found()
...   | |
129 | | |                             }
130 | | |                             )
    | |_|_____________________________^ expected generator, found a different generator
131 |   |                         }
132 |   |                     };
    |   |_____________________- `match` arms have incompatible types
    | 
   ::: /Users/maxwellflitton/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcore/future/mod.rs:55:43
    |
55  |     pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
    |                                               ------------------------------- the found opaque type
    |
    = note: expected type `std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>` (generator)
             found struct `std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>` (generator)

Estoy completamente atascado en esto. No sé cómo asegurarme de que sea un tipo. Si no hay una declaración de coincidencia y solo hay un bloque asincrónico, todo funciona bien. Esto es para middleware en un servidor web Actix. Estoy intentando redirigir al espectador si las credenciales no funcionan.

0
max89 18 oct. 2020 a las 17:56

1 respuesta

La mejor respuesta

Usa Box::pin para crear los futuros en caja, pero Rust todavía piensa que quieres la versión impl Future<...> (lo que significa que el futuro está en el montón, pero no usa el envío dinámico).

Es por eso que el tipo en el error es Pin<Box<impl Future<...>>>, y debido a que dos impl Future<...> s son de diferentes tipos, obtiene el error expected/found.

Debe decirle a Rust que está interesado en el envío dinámico (porque tiene dos futuros diferentes que se pueden almacenar en Box, y cuál realmente está almacenado allí solo se puede conocer en tiempo de ejecución), por ejemplo, especificando explícitamente el tipo de retorno como este:

use std::pin::Pin;
use std::future::Future;

let fresh_result: Pin<Box<dyn Future<Output=_>>> = match passed {
    // ...
}

Ahora obtendrá un nuevo error sobre cannot infer type for type parameter E que puede resolverse especificando también el Output del futuro, por lo que la nueva línea debería ser:

let fresh_result: Pin<Box<dyn Future<Output=Result<ServiceResponse, Error>>>> = match passed {
    // ...
}

Que se compilará con éxito!

1
pretzelhammer 24 dic. 2020 a las 23:58