tokio-rs / doc-push

Tokio doc blitz effort - A concerted effort to improve Tokio's documentation.
MIT License
50 stars 7 forks source link

one-liner example for aync read #99

Open ultrasaurus opened 4 years ago

ultrasaurus commented 4 years ago

Currently there is a TcpStream doc example of a one-liner that writes to a stream"

stream.write_all(b"hello world!").await?;

What would be an equivalent one-liner for read? (it could be read-N-bytes or read_until) I tried with read_lines, but it doesn't compile:

    stream.read_line(response).await?;

see full source, which fails with this error:

error[E0599]: no method named `read_line` found for type `tokio_net::tcp::stream::TcpStream` in the current scope
  --> src/main.rs:32:12
   |
32 |     stream.read_line(response).await?;
   |            ^^^^^^^^^
   |
   = note: the method `read_line` exists but the following trait bounds were not satisfied:
           `tokio_net::tcp::stream::TcpStream : tokio_io::io::async_buf_read_ext::AsyncBufReadExt`

How do I satisfy the trait bounds for AsyncBufReadExt? or is there a different / better approach?

krisselden commented 4 years ago
let mut response = String::new();
let reader = tokio::io::BufReader::new(stream);
reader.read_line(response).await?;
Ralith commented 4 years ago

Alternatively, with fewer moving parts but less flexibility:

let mut response = [0; 64];
stream.read_exact(&mut response).await?;
ultrasaurus commented 4 years ago

Thanks @krisselden for the read_line hint, this worked:

    let mut response = String::new();
    let mut reader = tokio::io::BufReader::new(stream);
    reader.read_line(&mut response).await?;
    println!("response: {}", response);

full code here

ultrasaurus commented 4 years ago

@Ralith I really like the one-liner... how would it work where 64 is a variable?

in my example, I'm expecting the following response (when I connect with my twitter account): :ultrasaurus_twitter!ultrasaurus_twitter@irc.gitter.im NICK :ultrasaurus_twitter

but "ultrasaurus_twitter" is dynamically determined by the USER environment variable, so I think I would want to do something like:

    let read_len = irc_user.len() * 3 + "!@irc.gitter.im NICK :".len() + 2;
    let mut response = String::with_capacity(read_len);
    stream.read_exact(&mut response).await?;
    println!("response: {}", response);

but I get this compile error:

error[E0308]: mismatched types
  --> src/main.rs:33:23
   |
33 |     stream.read_exact(&mut response).await?;
   |                       ^^^^^^^^^^^^^ expected slice, found struct `std::string::String`
   |
   = note: expected type `&mut [u8]`
              found type `&mut std::string::String`

I tried changing to Bytes, but still couldn't get it to work.... what syntax am I missing for this approach?

Ralith commented 4 years ago

read_exact is only suitable if you know the exact length of the data in advance, which is not the case for IRC. The type error you're getting can be resolved by using Vec<u8> or Bytes and passing it as &mut response[..], but for a protocol like IRC you should really use read_line instead. Or better yet, a version of read_line which bails out if it receives more than a maximum amount of data (iirc IRC caps out at 512 bytes per command) so that a malicious or buggy server can't DoS you.