2

In rust, I am trying to fulfill a future by extracting two bits of data out of a .get request using a hyper client as a tuple. The problem is the resulting type doesn't work.

So given some code like this:

let result = client
  .get(url)
  .map_err(|err| Error::from(err))
  .and_then(|response| {
    (response
      .into_body()
      .concat2()
      .map(|body| String::from_utf8(body.to_vec()).unwrap())
      .map_err(|err| Error::from(err)),
    response
      .headers()
      .get(CONTENT_TYPE)
      .map(|content_type| content_type.to_str()))
  });

I am getting an error like the trait "futures::IntoFuture" is not implemented for...

I am pretty sure that it is because the two members of the tuple are futures and can be dealt with, but a tuple isn't, but I am not sure how to resolve the values of the futures and place them into a tuple.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
Kitson
  • 1,650
  • 1
  • 18
  • 36

2 Answers2

1

The first element of your tuple is a future, but the second element is an Option. While IntoFuture is implemented for tuples as long as all elements implement it and the error types match, you only have a single future to resolve, so there is an easier solution.

Another problem is that response.into_body() consumes response, so you can't access it later to retrieve the headers. Since we have only a single future to resolve, the easiest solution is to extract the content type from the response first, and then attach it to the result in the map() method:

let result = client
    .get("https://www.stackoverflow.com/".parse().unwrap())
    .map_err(|err| Error::from(err))
    .and_then(|response| {
        let content_type = response
            .headers()
            .get(CONTENT_TYPE)
            .map(|content_type| content_type.to_str().unwrap().to_string());
        response
            .into_body()
            .concat2()
            .map(|body| (
                String::from_utf8(body.to_vec()).unwrap(),
                content_type,
            ))
            .map_err(|err| Error::from(err))
    });

Full example code in the playground

If you still have trouble making the code work, I suggest posting a question including a minimal example of the actual code you try compiling as well as the actual error message you get. Error messages for code using future combinators can get long and confusing, but they are still the most important bit of information to understand why the code doesn't compile.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • Thanks, while it does take a bit more manipulation for it to actually work for me, this does seem to explain the root cause of it. – Kitson Oct 18 '18 at 21:25
0

Your problem boils down to:

use futures::*;

struct Foo {}

impl Future for Foo {
    type Item = u32;
    type Error = u32;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(Async::Ready(100))
    }
}

fn main() {
    let f = Foo {};

    let _result = f
        .and_then(|val| {
            (val * 2, val * 4)
        });
}

That gives:

   |                                    
18 |  .and_then(|val| {
   |   ^^^^^^^^ the trait `futures::future::IntoFuture` is not implemented for `(u32, u32)`

By the way IntoFuture is implemented for Result:

impl<T, E> IntoFuture for Result<T, E>

And for the tuple:

impl<A, B> IntoFuture for (A, B) 
where
    A: IntoFuture,
    B: IntoFuture<Error = A::Error>,

And returning a tuple of Results works:

use futures::*;

struct Foo {}

impl Future for Foo {
    type Item = u32;
    type Error = u32;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        Ok(Async::Ready(100))
    }
}

fn main() {
    let f = Foo {};

    let _result = f
        .and_then(|val| {
            (Ok(val * 2), Ok(val * 4))
        });
}

playground with your example: it works but it is quite convoluted to obtain the results. See also the comments below.

attdona
  • 17,196
  • 7
  • 49
  • 60
  • Thanks for pointing out that a tuple of futures can be returned directly, and is implicity joined by the `IntoFuture` implementation for tuples. However, I believe your example code is wrong; `response.into_body().concat2()` already _is_ a future. You wrap that future in `Ok()`, which results in a future resolving immediately and returning the unresolved future. I don't think this is useful in any way. – Sven Marnach Oct 18 '18 at 14:31
  • Moreover, if you really wanted to return a tuple of values that both aren't futures, it would be rather pointless to chain them on with `and_then()` by artificially turning them into futures. It would be a lot easier to simply use `map()` instead. – Sven Marnach Oct 18 '18 at 14:34