if-statementrustactix-web

Respond with either a Redirect or a NamedFile in actix-web


I have a small backend app written in Rust using actix-web. I want to have a function similar to the following.

async fn function(var: bool) -> impl Responder {
    if var {
        Redirect::to("link")
    } else {
        NamedFile::open_async("/file").await
    }
}

I get the error :`if` and `else` have incompatible types expected `Redirect`, found future.

I think this can probably be done by reading the file into a string and using HttpResponse. But since NamedFile implements Responder, is it possible to make it work in this setting?

If I get rid of the if-else, each of these are valid return types, but the return types of an if-else statement must match. So, is it possible to maybe not use the if-else in this situation?

Edit: The linked solution doesn't work in this case. Changing the impl Responder to Box<dyn Responder> causes the trait bounds to fail. I get the following error message:

the trait bound `std::boxed::Box<(dyn actix_web::Responder + 'static)>: actix_web::Responder` is not satisfied 

Maybe it can be fixed, but I don't have enough experience with Rust generics to do it.


Solution

  • Dynamic dispatch cannot work with Responder, but there are two other solutions.

    The first is using Either, that also implements Responder by forwarding to the active variant:

    use actix_web::Either;
    
    async fn function(var: bool) -> impl Responder {
        if var {
            Either::Left(Redirect::to("link"))
        } else {
            Either::Right(NamedFile::open_async("/file").await)
        }
    }
    

    A second option, as you said, is to convert them to HttpResponse. They have different body types, so we also need to convert the bodies:

    async fn function(var: bool, request: HttpRequest) -> HttpResponse {
        if var {
            Redirect::to("link")
                .respond_to(&request)
                .map_into_boxed_body()
        } else {
            NamedFile::open_async("/file")
                .await
                .respond_to(&request)
                .map_into_boxed_body()
        }
    }
    

    HttpRequest is an extractor, so you can just have this parameter in your handler function.