Search code examples
typesrusttype-annotationrust-warp

Warp asks for absurdly long and complex explicit type annotations, is there another way?


I'm getting the following error,

error[E0283]: type annotations needed for `warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::Filter+std::marker::Copy, impl warp::Filte
r+std::marker::Copy>, impl warp::Filter+std::marker::Copy>, [closure@src/http.rs:12:13: 24:4]>`
  --> src/http.rs:12:4
   |
9  |     let create_user = warp::post()
   |         ----------- consider giving `create_user` the explicit type `warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::Filter+std:
:marker::Copy, impl warp::Filter+std::marker::Copy>, impl warp::Filter+std::marker::Copy>, [closure@src/http.rs:12:13: 24:4]>`, with the type parameters specified
...
12 |         .and_then(|user: super::user::User| async move {
   |          ^^^^^^^^ cannot infer type
   |
   = note: cannot satisfy `_: reject::sealed::CombineRejection<Rejection>`

This is what I wrote. I'm confused at how this is supposed to look,

pub async fn users() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
  let create_user = warp::post()
    .and(warp::path::end())
    .and(warp::body::json())
    .and_then(|user: super::user::User| async move {
      match &user.id_user {
        None => {
          if let Ok(u) = user.insert().await {
            Ok(warp::reply::json(&u))
          }
          else {
            Ok(warp::reply::json(&"FOO".to_owned()))
          }
        }
        Some(_) => Ok(warp::reply::json(&"FOO".to_owned())),
      }
    });

  let routes = warp::path("users");
  routes.and(create_user)
}

How is this supposed to look, am I really supposed to use an explicit type like,

warp::filter::and_then::AndThen<warp::filter::and::And<warp::filter::and::And<impl warp::Filter+std::marker::Copy, impl warp::Filter+std::marker::Copy>, impl warp::Filter+std::marker::Copy>

Just to be clear what I want here, is a create user end point.

  • I want a user object to be deserialized from JSON
  • I want to be able to reject the request (HTTP error) if that user object after deserialization has an id. I don't want the user to be able to choose the ID.
  • &user.insert() will insert the user object in the database, and return a new user object with the ID.
  • I want the either an error or the user object to be returned to the user.

How can I make sense of the absurdly complex type annotations that Warp is requesting? Is this really required, or am I making mistake?


Solution

  • The issue here is failing type inference for the async block in the and_then() closure. There is no Err() path which would tell the compiler what the Error variant would be, thus the inference fails. You can fix this by annotating the full Result type on one of the return branches:

    Ok::<_, warp::Rejection>(warp::reply::json(&u))
    

    For additional reference:

    https://users.rust-lang.org/t/async-function-parameter-results-in-type-annotation-error/45379

    and a workaround for similar issue about using ? in async blocks:

    https://rust-lang.github.io/async-book/07_workarounds/02_err_in_async_blocks.html