max89
max89

Reputation: 463

Rust expected type found struct

I've got the following code:

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
}

However, I get the following 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)

I am completely stuck on this. I don't know how to ensure it's a type. If there is no match statement and there is just one async block then it all runs fine. This is for middleware in an Actix-web server. I am trying to redirect the viewer if credentials are not working.

Upvotes: 0

Views: 1535

Answers (1)

Ohad
Ohad

Reputation: 2618

You use Box::pin to create the boxed futures, but Rust still thinks you want the impl Future<...> version (meaning the future is boxed to the heap, but doesn't use dynamic dispatch).

This is why the type in the error is Pin<Box<impl Future<...>>>, and because any two impl Future<...>s are of different types, you get the expected/found error.

You need to tell Rust you are interested in dynamic dispatch (because you have two different futures that can be stored in the Box, and which one really is stored there can only be known at runtime), for example by explicitly specifying the return type like this:

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

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

Now you will get a new error about cannot infer type for type parameter E which can be resolved by specifying the Output of the future as well, so the new line should be:

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

which will compile successfully!

Upvotes: 1

Related Questions