One type is more general than the other

I have an async fn that returns a value I pass to Tokio's tokio::spawn() that compiled, but had a bug. Trying to fix the bug by modifying a statement in the function caused the program to not compile anymore. I'm using the alpha version of Tokio (the one that supports async/.await). The error is:

error[E0308]: mismatched types
--> src/wormhole.rs:55:9
|
55 | tokio::spawn(session);
| ^^^^^^^^^^^^ one type is more general than the other
|
= note: expected type tokio_io::io::shutdown::Shutdown<'_, wormhole::appendmsg_session::ClonedWriter<'_>>
found type tokio_io::io::shutdown::Shutdown<'_, wormhole::appendmsg_session::ClonedWriter<'_>>

What it found and what it expected look identical. The gist of what I changed was altering the types of the futures passed to a call to try_join(), which is awaited inside that function. It used to be:

    try_join(
        DecryptingReader::new(remote_header, &mut client_reader).copy(&mut server_writer),
        EncryptingReader::new(*header, &mut server_reader).copy(&mut client_writer),
    )
    .await?;

The types of those arguments are:

    tokio_io::io::copy::Copy<'_, appendmsg_session::DecryptingReader<'_>, tokio_net::tcp::split::WriteHalf<'_>>
    tokio_io::io::copy::Copy<'_, appendmsg_session::EncryptingReader<'_>, tokio_net::tcp::split::WriteHalf<'_>>

In the not-compiling version, the arguments are more complex, because I tacked on a .then combinator:

    try_join(
        DecryptingReader::new(remote_header, &mut client_reader)
            .copy(&mut server_writer)
            .then(|r| {
                reader_finish(r);
                server_writer_to_close.shutdown()
            }),
        EncryptingReader::new(*header, &mut server_reader)
            .copy(&mut client_writer)
            .then(|r| {
                reader_finish(r);
                client_writer_to_close.shutdown()
            }),
    )
    .await?;

The types of the arguments are now:

    futures_util::future::then::Then<tokio_io::io::copy::Copy<'_, appendmsg_session::DecryptingReader<'_>, appendmsg_session::ClonedWriter<'_>>, tokio_io::io::shutdown::Shutdown<'_, appendmsg_session::ClonedWriter<'_>>, [closure@src/appendmsg_session.rs:49:19: 52:14 server_writer_to_close:_]>`
    futures_util::future::then::Then<tokio_io::io::copy::Copy<'_, appendmsg_session::EncryptingReader<'_>, appendmsg_session::ClonedWriter<'_>>, tokio_io::io::shutdown::Shutdown<'_, appendmsg_session::ClonedWriter<'_>>, [closure@src/appendmsg_session.rs:56:19: 59:14 client_writer_to_close:_]>`

What does that error mean? I'm not sure how to go about resolving this one.

It seems like there's a github issue about this.

looks like it, although they haven't worked on it in a while. It's pretty esoteric stuff.

Esoteric? Maybe, I suppose... but I hit this a lot when I'm trying to refactor code using async. I've started reading it as "welp, you don't get to use closures anymore, figure out how to express that as a named function where you can provide for<'a>."

What's the right protocol for voting on a github issue? Commenting with "lol me too" feels rude.

Try marking your closures move to make them take ownership of server_writer_to_close and client_writer_to_close.

As far as I understand it has something to do with the closures being valid for one specific lifetime, while needing to be valid for any lifetime.

I did try that. The following error results:

returns a value referencing data owned by the current function
`client_writer_to_close` is borrowed here

Not sure why the move elicits that. The shutdown() method takes a &self. I assumed that "current function" was the async function containing all this and also I assumed that meant that the move meant the variable would now be owned by the closure. Wrong on both counts, perhaps.

It's true that this might have to be done in a different way.

The upshot is that the application is joining on a pair of futures representing TCP connections, only one of which will complete without manual intervention. For both futures, "completion" means the "TCP stream has ended". What I wanted is: on the completion of one of the futures, issue a shutdown that trigger the completion of the other one. Waiting for them both with try_join and forcing the trigger via a then combinator sounded reasonable, but may not be a good way to model this behavior.

As far as upvoting github issues, I thought there was a thumbs up or something (maybe it should be a "dislike button...)