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:
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:
Self::Item
and return some type B
B
returned by your closure must be convertible into a future.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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With