Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The trait bound `(): futures::Future` is not satisfied when using TcpConnectionNew

I am trying to write a simple TCP client in Rust using Tokio crate. My code is pretty close to this example minus the TLS:

extern crate futures;
extern crate tokio_core;
extern crate tokio_io;

use futures::Future;
use tokio_core::net::TcpStream;
use tokio_core::reactor::Core;
use tokio_io::io;

fn main() {
    let mut core = Core::new().unwrap();
    let handle = core.handle();

    let connection = TcpStream::connect(&"127.0.0.1:8080".parse().unwrap(), &handle);

    let server = connection.and_then(|stream| {
        io::write_all(stream, b"hello");
    });

    core.run(server).unwrap();
}

However, compilation fails with the error:

error[E0277]: the trait bound `(): futures::Future` is not satisfied
  --> src/main.rs:16:29
   |
16 |     let server = connection.and_then(|stream| {
   |                             ^^^^^^^^ the trait `futures::Future` is not implemented for `()`
   |
   = note: required because of the requirements on the impl of `futures::IntoFuture` for `()`

error[E0277]: the trait bound `(): futures::Future` is not satisfied
  --> src/main.rs:20:10
   |
20 |     core.run(server).unwrap();
   |          ^^^ the trait `futures::Future` is not implemented for `()`
   |
   = note: required because of the requirements on the impl of `futures::IntoFuture` for `()`

I find it strange because according to the documentation it should be implemented.

I'm using

  • Rust 1.19.0
  • futures 0.1.16
  • tokio-core 0.1.10
  • tokio-io 0.1.3

What am I missing?

like image 874
Sergey Avatar asked Oct 07 '17 21:10

Sergey


Video Answer


2 Answers

Unfortunately the answer here is quite specific, but the question turns up for any kind of search for:

the trait futures::Future is not implemented for ()

A typical scenario for this kind of error is:

foo.then(|stream| {
    // ... Do random things here
    final_statement();
});

This causes an error because the majority of the extension functions require the return type to implement IntoFuture. However, () does not implement IntoFuture, and by terminating the block with a ; the implicit return type is ().

However, IntoFuture is implemented for Option and Result.

Rather than just randomly removing semicolons vaguely in the hope this will somehow magically make your code compile, consider:

You should be returning something that can be converted into a Future using IntoFuture.

If you don't have a specific promise that you're returning, consider returning Ok(()) to say simply 'this is done' from your callback:

foo.then(|stream| {
    // ... Do random things here
    final_statement();
    return Ok(()); // <-- Result(()) implements `IntoFuture`.
});

Note specifically I terminate this block with an explicit return statement; this is deliberate. This is a typical example of how the ergonomics of 'can omit semicolon to implicitly return object' is tangibly harmful; terminating the block with Ok(()); will continue to fail with the same error.

like image 99
Doug Avatar answered Feb 12 '23 12:02

Doug


TL;DR: remove the semicolon after io::write_all.


Review the definition of and_then:

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, 

The closure (F) has to return some type (B) that can be converted into a future (B: IntoFuture) with an error type that matches the starting closure (Error = Self::Error).

What does your closure return? (). Why is that? Because you've placed a semicolon (;) at the end of your line. () does not implement the trait IntoFuture, which is indicated by the error message part "on the impl of futures::IntoFuture for ()":

impl<F: Future> IntoFuture for F {
    type Future = F;
    type Item = F::Item;
    type Error = F::Error;
}

Removing the semicolon will cause the Future returned by io::write_all to be returned back to and_then and the program will compile.

In general, futures work by combining together smaller components which are themselves futures. All of this works together to build a single large future which is essentially a state machine. It's good to keep this in mind, as you will almost always need to return a future when using such combinators.

like image 44
Shepmaster Avatar answered Feb 12 '23 14:02

Shepmaster