Why I have to use tokio::net::TcpStream.split() for concurrent read/writes

I am looking at tokio::net::TcpStream class. Looks like if I need to do concurrent reads and writes, I have to split() and get references to read and write halves.

I see that tokio::net::TcpStream implements both AsyncRead and AsyncWrite. My question is why I can't do concurrent read/writes on depending on these AsyncRead and AsyncWrite implementations?

Minor terminology fix: we don't have any classes. It's just types, or struct/enum when it's more appropriate. For me it was crucial to use the term "class" to understand the Rust better.

tl;dr: No you don't have to, it's just more convenient.

Checkout the signature of the methods of the AsyncRead/AsyncWrite traits. They have poll_* methods which checks readiness of the IO operation, without blocking the thread. It's possible to implement std::future::Future manually on your custom type which read/write concurrently without split it. But to do so you need to understand how exactly the Rust's async system works, sometimes requires deep understanding of the underlying executors and the pinning semantics. And there's no guarantee that the result of such efforts would outperforms the solution with .split().

You certainly can, in fact the generic tokio::io::split function does exactly that by wrapping the given resource in an Arc<Mutex<...>> and having polls on each half call the two kinds of polls in an interleaved manner.

However, the generic version must use a Mutex. Why? The poll functions take &mut self, so you must have exclusive access to poll the io resource in either way.

On the other hand, the dedicated split and into_split functions on TcpStream do not use a Mutex, because they have the special knowledge that they are talking to a tcp stream, and your OS has no issue with parallel reading and writing. The generic one cannot do this, since the type might not have that capability.

Why does tokio::io::split exist? Some work flows are much easier to write when you can split it.

Why does &TcpStream (or Arc<TcpStream>) not implement AsyncRead + AsyncWrite? Because although Tokio supports both reads and writes in parallel, we don't support two parallel reads, or two parallel writes, which splitting still doesn't allow.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.