I need to implement AsyncRead and AsyncWrite but dont know where to get started

Well either that, or you would need to use a boxed future to wrap the async/await.

well in that case there will be still heap allocations every time. Isnt async_trait just doing that?

Ah I understand now. With a poll-based trait, you can avoid all but one of them with the ReusableBoxFuture from earlier.

also on top of that I have no clue how to turn Poll into a Future that can be awaited by the last user and where i would get the context thingy

When you implement the Future trait on a type, you need to define the following method:

fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>

The user can then use it from async/await by just calling .await on your custom struct. This works because any struct that implements Future can be awaited — the compiler will take care of calling poll for you when you await something. As for the context thingy, you get it as an argument to the method you are implementing on the trait.

When it comes to other poll-based traits like AsyncRead, it is awaited by the user through the *Ext traits. For example, if you implement the poll-based trait AsyncRead on MyAwesomeSocket, all methods from AsyncReadExt are automatically available for MyAwesomeSocket. The user can then call AsyncReadExt::read on your socket, which returns a custom struct that implements Future. The user can then .await that future like this:

my_awesome_socket.read(some_args_go_here).await;

The above will then turn into something that calls poll on the future returned by read, which will in turn call poll_read on your socket. You can find the source code for the utility struct returned by AsyncReadExt::read here: tokio/src/io/util/read.rs.

1 Like

I cant thank you enough for the detailed answers you provided. For my workflow I will still go with async_trait for now since I need to get this to prod fairly quickly but and allocation cost at this moment doesnt really bother me much But I think I understand the idea.

  • create something Like MyRead that is returned from self.read
  • implement the Future for that
  • its the executors to provide the context
  • it is self s problem where to store the future returned from .read aka MyRead
  • in poll method of MyRead call .poll of the future returned by underlaying future possibly returned by tokio::io and return the resulting Poll back

I am not missing anything right ?

Normally you would not make your own MyRead future. You would either use the one from Tokio, or you would be sidestepping poll methods entirely. Otherwise it seems right.

Just checked tokio's AsyncReadExt impl for TcpStream which calls which returns a Read of some sort but seems none of those structs are public so I would have to make my own Read future anyway seems like.
How would I sidestep poll ? isnt it the only way if i am to implement poll based futures instead of Box<Pin<dyn Future>>

The privacy of the struct should not be an issue. Anyone can implement AsyncRead, and all implementors of AsyncRead get AsyncReadExt for free. The automatic impl of AsyncReadExt is part of Tokio, so it can access the private struct.

You sidestep poll by using async_trait like you have been talking about doing.

Oh i see thanks again for all the info

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.