1

Consider this code snippet:

use std::net::SocketAddr;
use tokio::sync::mpsc::{channel, Receiver, Sender}; // 0.1.22
use tokio::{net::TcpListener, prelude::*}; // 0.1.22

fn main() {
    let addr = "127.0.0.1:8118".parse::<SocketAddr>().unwrap();
    let listener = TcpListener::bind(&addr).expect("unable to bind");
    let (sender, _): (Sender<char>, Receiver<char>) = channel(64);

    tokio::run(
        listener
            .incoming()
            .and_then(|s| tokio::io::read_to_end(s, vec![]))
            .map_err(|e| panic!("failed: {:?}", e))
            .for_each(move |(_socket, buf)| {
                let s = sender.clone();
                println!("Received: {:#?}", buf);
                let c = buf[0] as char;
                s.send(c).map_err(|e| panic!("failed: {:?}", e));
                Ok(())
            }),
    );
}

When building, I get this warning:

warning: unused `futures::future::map_err::MapErr` that must be used
  --> src/main.rs:19:17
   |
19 |                 s.send(c).map_err(|e| panic!("failed: {:?}", e));
   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
   |
   = note: #[warn(unused_must_use)] on by default
   = note: futures do nothing unless polled

I assume I have to do something Tokio / async related, but I can't figure out what. I assumed that tokio::run would handle all of the future related problems by running the code block within in the event loop, but it doesn't seem like it. Do I need to do something special in this case?

Playground link.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Daniel Porteous
  • 5,536
  • 3
  • 25
  • 44
  • just remove `Ok(())` and the last `;`, https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=0307fa47b87e7157b360d23469db4e4b, BTW tokio is quite hard not the best way to learn rust – Stargateur Jul 07 '19 at 00:52
  • Thanks! In that case, what do I do if want to do it conditionally. For example, see [this code](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=9307ebca83ea3d9c804e7f501b23561d). Does tokio support conditionally not returning a future? I feel like making a new futures map is not the correct approach. – Daniel Porteous Jul 07 '19 at 01:03
  • 1
    https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=324ab47d35ec7f332469b6ee0d2de931, "I feel like making a new futures map is not the correct approach" there is a lot of way and I'm clearly not a expert in tokio. – Stargateur Jul 07 '19 at 01:15
  • Nice that worked :D I agree, Tokio seems like no more than a rather unintuitive stepping stone to native async. I hope the core Rust team go in a different direction. – Daniel Porteous Jul 07 '19 at 01:41
  • async and away are still a work in progress, future is now stable https://blog.rust-lang.org/2019/07/04/Rust-1.36.0.html#the-future-is-here. You could try to use away and async in nightly mode see https://stackoverflow.com/a/52835926/7076153 – Stargateur Jul 07 '19 at 01:46

1 Answers1

2

The same way as any "unused" warning: either use the value or delete it.

Here, use tokio::write_all to write the first byte to the socket, returning a new future. This is called in an and_then combinator:

use std::net::SocketAddr;
use tokio::sync::mpsc::{channel, Receiver, Sender}; // 0.1.22
use tokio::{net::TcpListener, prelude::*}; // 0.1.22

fn main() {
    let addr = "127.0.0.1:8118".parse::<SocketAddr>().unwrap();
    let listener = TcpListener::bind(&addr).expect("unable to bind");
    let (sender, _): (Sender<char>, Receiver<char>) = channel(64);

    tokio::run({
        listener
            .incoming()
            .and_then(|s| tokio::io::read_to_end(s, vec![]))
            .map_err(|e| panic!("failed: {:?}", e))
            .inspect(|(_socket, buf)| println!("Received: {:#?}", buf))
            .and_then(move |(_socket, buf)| {
                let c = buf[0] as char;
                sender.clone().send(c).map_err(|e| panic!("failed: {:?}", e))
            })
            .for_each(|_| Ok(()))
    });
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • This answer changes what the code does, I don't want to write back to the socket. The sender I'm using is from tokio itself, so why is sender.send a blocking call? There must be a way to put something in a channel asynchronously. Thanks! – Daniel Porteous Jul 08 '19 at 06:50
  • @DanielPorteous yep, I got mixed up between the socket and the channel, sorry! Even worse, I thought that `send` method was on the socket (even though it only exists on a `UdpSocket`, not `TcpStream` and this isn't even a standard library `TcpStream` but a Tokio `TcpStream`...). This `send` method is the one from the [`Sender`](https://docs.rs/tokio/0.1.22/tokio/sync/mpsc/struct.Sender.html) trait, so it *is* indeed asynchronous. However, there are methods on types from Futures / Tokio that are blocking, so we can't just assume they are good to use. – Shepmaster Jul 08 '19 at 11:51