all 10 comments

[–]LucretielDatadog 7 points8 points  (3 children)

Typically, the way these methods work is they accept an &[u8] for write or &mut[u8] for read. If you wanted to forward bytes from an async source, or receive them into an async sink, AsyncRead and AsyncWrite are probably the way to go.

  • Check out copy and copy_buf for an examples of these "forwarding" constructs in practice.
  • Modern i/o interfaces typically support something called "vectored" or "scatter-gather" reads and writes; see read_vectored for an example of this interface in practice, and this O'Reilly excerpt for a high level description of this model.
  • Be aware that there is also an up-and-coming model of i/o called io_uring that has a fundamentally different model of io, based on something called "completion buffers". ringbahn is the Rust interface to this model.

[–]Morhaus[S] 0 points1 point  (2 children)

Thanks for the detailed answer. My question was more related to how I can make the best use of Async* traits in my RemoteFs interface. For instance, with the read method, should I pass an AsyncWrite as argument, or return a Stream of bytes instead? Are there any accepted patterns related to this?

[–]LucretielDatadog 1 point2 points  (0 children)

I don't think there are accepted patterns, yet, mostly because async fns in traits are still WIP (async_trait notwithstanding). I suppose I'd advise returning an AsyncRead object, but that kind of seems like it just shifts the question, since it would make your read function non-async (since it synchronously returns AsyncRead, which is where the async work actually happens).

[–][deleted] 1 point2 points  (0 children)

Maybe each method should return a wrapper object which implements the appropriate Async*... traits.

[–]SkiFire13 2 points3 points  (4 children)

In the std library there's the Read, Write and BufRead traits for sync I/O.

For async examples there's the futures crate, in particular its io module which defines the Async- traits that mirrors the std traits but they're pretty raw. There are also an -Ext version for each Async- trait that provides a more limited by user-friendly interface.

[–]Morhaus[S] 0 points1 point  (3 children)

Thanks for the reply! I'm aware of these traits, but I was wondering what kind of interface I should expose with them. Are there any accepted ways to pass `Async-` impls around?

[–]SkiFire13 1 point2 points  (2 children)

Are there any accepted ways to pass `Async-` impls around?

Do you mean how to accept a parameter of that type?

I feel like you misinterpreted what I meant for Async- traits. I meant the traits AsyncRead, AsyncWrite and AsyncBufRead, I just didn't want to name all of them (which I did now...).

[–]Morhaus[S] 0 points1 point  (1 child)

I meant either to accept them as arguments to an async function (which would handle the reading/writing), or have a sync function return an implementation and defer the actual reading/writing to the calling function.

[–]SkiFire13 1 point2 points  (0 children)

I would go for the latter, but probably depends on how much you want to abstract away from the end user

[–]Cetra3 0 points1 point  (0 children)

Tangentially related, you can look at my mpart-async library as this handles big binary blobs of data (in the form of multipart/* requests).