1

I am trying to send a message to one db handler, and based on the result send a message to a second handler, or return an error from the first.

What I've come up with so far doesn't work; rustc says match arms have incompatible types

expected struct 'futures::future::and_then::AndThen', found enum 'std::result::Result'

state
    .db
    .send(...)
    .from_err()
    .and_then(|res| match res {
        Ok(response) => {
        // Do some additional logic here
        state
        .db
        .send(...)
        .from_err()
        .and_then(|res| match res {
            Ok(response) => Ok(HttpResponse::Ok().json(response)),
            Err(err) => Ok(HttpResponse::InternalServerError().body(err.to_string()))
            }) 
        },
        Err(err) => Ok(HttpResponse::InternalServerError().body(err.to_string()))
    })
    .responder()

Question how to accomplish this in actix-web?

trolle3000
  • 1,067
  • 2
  • 14
  • 27
  • Please review how to create a [MCVE] and then [edit] your question to include it. We cannot tell what crates, types, traits, fields, etc. are present in the code. Try to produce something that reproduces your error on the [Rust Playground](https://play.rust-lang.org) or you can reproduce it in a brand new Cargo project. There are [Rust-specific MCVE tips](//stackoverflow.com/tags/rust/info) as well. – Shepmaster Jan 14 '19 at 02:25

1 Answers1

2

You were almost there.

When using a match, all arms must yield the same type. In your case, one is a future combined with and_then, on the other arm, you have a result.

Furthermore, unless your send() function returns a type impl Future<Item = Result<R, E>, Error = E>, the match is completely superfluous. and_then takes as parameter Item, not Result<Item, Error>.

So, the entire thing can just be reduced to:

state
  .db
  .send(...)
  .from_err()
  .and_then(|res| state.db.send(...).from_err())
  .then(|res| match res {
     Ok(response) => Ok(HttpResponse::Ok().json(response)),
     Err(err) => Ok(HttpResponse::InternalServerError().body(err.to_string()))
   })
   .responder()

Let's assume your types are correct, though. You can easily convert that Result into a future with future::result like so:

state
    .db
    .send(...)
    .from_err()
    .and_then(|res| future::result(res).from_err())
    .and_then(|res| 
        // Do some additional logic here
          state
          .db
          .send(...)
          .from_err()
     )
     .and_then(|res| future::result(res).from_err())
     .then(|res| match res {
       Ok(response) => Ok(HttpResponse::Ok().json(response)),
       Err(err) => Ok(HttpResponse::InternalServerError().body(err.to_string()))
      })
     .responder()

An example is on the sandbox there: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6801886b02081160e268f395bcc1ad6c

Sébastien Renauld
  • 19,203
  • 2
  • 46
  • 66
  • Thanks for your reply @sébastien-renauld! It seems it will take a bit of extra work to get either example to work, I'll keep you posted :-) – trolle3000 Jan 13 '19 at 00:23
  • @trolle3000 Can you give me the return type of your `send()` function? That is the detail that's missing from your question, that prevents me from being able to give you a direct answer. – Sébastien Renauld Jan 13 '19 at 00:25
  • Hi @Sébastien Renauld, the return type is `Result` where `Error` is an `actix_web::Error` and `models::Gpio` is a struct (defined here: https://gitlab.com/bogeholm/raspberry-web/blob/master/src/models.rs) – trolle3000 Jan 13 '19 at 19:54
  • With the appropriate type annotations it works perfectly! Thanks :-) – trolle3000 Jan 13 '19 at 19:58