I have a variable that will control which function is the default behavior of my web app, but both functions are async
and it wont let me because they're different closures. Something like this reproduces the same error:
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=db0269ef4852a94438e6d4925ca83d3b
Now my questions are:
main.rs
// Matches comes from the command line
let a = match matches.occurrences_of("default"){
1 => handlers::forward,
_ => handlers::resource_not_found,
};
... setting up db, auth etc
HttpServer::new(move || {
App::new()
... lots of routes
.default_service(web::route().to(a))
handlers.rs
pub async fn resource_not_found(
) -> Result<HttpResponse, Error> {
Ok(HttpResponse::NotFound().body("Resource does not exist"))
}
pub async fn forward(
) -> Result<HttpResponse, Error> {
Ok(HttpResponse::Ok().body("Resource does exist"))
}
How do I solve this so that the match works
You can create two closures that both return a boxed future, and coerce them to a function pointer:
use std::pin::Pin;
use std::future::Future;
let a: fn() -> Pin<Box<dyn Future<Output = Result<HttpResponse, Error>>>> =
match matches.occurrences_of("default") {
1 => || Box::pin(handlers::forward()),
_ => || Box::pin(handlers::resource_not_found()),
};
If the functions really are that simple, then you can avoid the boxing and return std::future::Ready
:
use std::pin::Pin;
use std::future::{Future, Ready, ready};
let a: fn() -> Ready<Result<HttpResponse, Error>> =
match matches.occurrences_of("default") {
1 => || handlers::forward,
_ => || handlers::resource_not_found,
};
// handlers.rs
pub fn resource_not_found(
) -> Ready<Result<HttpResponse, Error>> {
ready(Ok(HttpResponse::NotFound().body("Resource does not exist")))
}
pub fn forward(
) -> Ready<Result<HttpResponse, Error>> {
ready(Ok(HttpResponse::Ok().body("Resource does exist")))
}
Is there a better way to set default server behavior programmatically on actix based on user CLI flags
You could do the matching inside a new handler instead of outside:
let x = matches.occurrences_of("default");
let a = move || async move {
match x {
1 => handlers::forward().await,
_ => handlers::resource_not_found().await,
}
};
Now instead of an extra allocation and dynamic dispatch per call, you just have to match on an integer.