Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

understanding error: trait `futures::future::Future` is not implemented for `()`

This question is about how to read the Rust documentation and improve my understanding of Rust so as to understand how to address this specific compiler error.

I've read the tokio docs and experimented with many of the examples. In writing my own code, I frequently run into compiler errors that I don't understand and often found I can fix the code, but don't understand why specific syntax is needed.

I reproduced with a very simple example based on tokio's hello world:

use futures::Future;
use tokio::net::TcpStream;
use tokio::prelude::*;

fn main() {
  let addr = "127.0.0.1:6142".parse().unwrap();

  let client = TcpStream::connect(&addr).and_then(|stream| {
      println!("created stream");
      // Process stream here.

      // Ok(())
  });

}

The above code is incorrect, requiring the commented out Ok(). I know that this is true, but not exactly why. This is perhaps the other half of a prior question How do I interpret the signature of read_until and what is AsyncRead + BufRead in Tokio? -- now I understand closures better, but can't quite parse the docs to understand the expected return value.

When I attempt to compile the incorrect code above, I get the following error:

error[E0277]: the trait bound `(): futures::future::Future` is not satisfied
 --> tokio-chat-client/src/main.rs:8:42
  |
8 |   let client = TcpStream::connect(&addr).and_then(|stream| {
  |                                          ^^^^^^^^ the trait `futures::future::Future` is not implemented for `()`
  |
  = note: required because of the requirements on the impl of `futures::future::IntoFuture` for `()`

There are two parts to my question:

  1. What is the error message trying to tell me?
  2. How would I use the docs for and_then to understand the expected return value?
like image 962
Ultrasaurus Avatar asked Aug 30 '19 03:08

Ultrasaurus


2 Answers

The docs for and_then state that:

fn and_then<F, B>(self, f: F) -> AndThen<Self, B, F> where
    F: FnOnce(Self::Item) -> B,
    B: IntoFuture<Error = Self::Error>,
    Self: Sized, 

This means that:

  • Your closure must accept an argument of type Self::Item and return some type B
  • The type B returned by your closure must be convertible into a future.
  • And if that future returns an error, then that error must have type Self::Error.

Moreover, if you look at the doc for IntoFuture, you will see that it is implemented for Result, so it works for Ok(()), but that it is not implemented for (), so it doesn't work if your closure returns nothing.

like image 55
Jmb Avatar answered Nov 05 '22 05:11

Jmb


Basically the closure you pass to and_then has the wrong type. It expects:

F: FnOnce(Self::Item) -> B

But you give it a closure of unit type, i.e. returns no value. Hence the error.

That said, the rustc error message isn't optimal here. It would be much better if it reads:

let client = TcpStream::connect(&addr).and_then(|stream| {
    println!("created stream");
    // error: mismatched types: expected `IntoFuture` but found `()`
});

The rust-lang project has this ticket to track the progress on said diagnostics issue.

like image 2
edwardw Avatar answered Nov 05 '22 05:11

edwardw