-
Notifications
You must be signed in to change notification settings - Fork 341
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Can we simplify io::copy? #365
Comments
I believe we could make the same change in the standard library. scratches head |
@stjepang I'd be in favor of trying this out — it seems you've thought this through and this seems good. It might be interesting to open an issue for this in std also! |
I think the main motivation behind forcibly taking a reference is to show intended use, I don't see much use for an API where both parameters are passed owned if they are streams. I'm not sure if this is a good motivation for such an API, as accidentally passing the stream owned will be caught by the borrow checker in all cases it is a problem. I'm fine with trying out this change. |
It's interesting how the pub fn copy<R, W>(reader: R, writer: &mut W) -> Copy<R, W> where
R: AsyncRead,
W: AsyncWrite + Unpin + ?Sized; Source: https://docs.rs/futures/0.3.0/futures/io/fn.copy.html So |
What are your feelings on this? People are repeatedly complaining about Tokio has the With the change proposed here, everything that works in |
Still happy to try this change out! edit: we should probably mark it as "unstable" though, at least for one release cycle to allow us to change our minds. |
I saw another issue mentioning a let conn = TcpStream::connect(&self.addr).await?;
let (r, w) = conn.split(); // Or some variation of this function.
self.reader = BufReader::new(r);
self.writer = BufWriter::new(w); This way I can have a bi-directional flow between my client and server that is buffered. |
@gsquire you can put |
@yoshuawuyts I ran into borrowing issues since this was inside of a function that was making a new type. I'll re-evaluate and see where I end up. Maybe the bufstream crate will support |
Could you elaborate @yoshuawuyts ? |
I believe On phone now, so haven't had a chance to test this out. But afaik people have done this successfully before. |
@yoshuawuyts The issue is that |
This problem has a straightforward solution. :) We just need to implement let conn = Arc::new(TcpStream::connect(&self.addr).await?);
self.reader = BufReader::new(conn.clone());
self.writer = BufWriter::new(conn); |
@stjepang Is that possible with the orphan rules ? |
edit: this is wrong, figured it out in #365 haha @sebastiencs just tried this out, and it seems this is indeed not possible. Target programuse async_std::io::{self, BufReader, BufWriter};
use async_std::net::TcpStream;
use async_std::sync::Arc;
use async_std::task;
fn main() -> io::Result<()> {
task::block_on(async {
let addr = "localhost:8080";
let conn = Arc::new(TcpStream::connect(&addr).await?);
let reader = BufReader::new(conn.clone());
let writer = BufWriter::new(conn);
Ok(())
})
} async-std implI added this to impl Read for std::sync::Arc<TcpStream> {
fn poll_read(
self: Pin<&mut Self>,
cx: &mut Context<'_>,
buf: &mut [u8],
) -> Poll<io::Result<usize>> {
self.watcher.poll_read_with(cx, |mut inner| inner.read(buf))
}
} Errorerror[E0117]: only traits defined in the current crate can be implemented for arbitrary types
--> src/net/tcp/stream.rs:367:1
|
367 | impl Read for std::sync::Arc<TcpStream> {
| ^^^^^^^^^^^^^^-------------------------
| | |
| | `std::sync::Arc` is not defined in the current crate
| impl doesn't use only types from inside the current crate
|
= note: define and implement a trait or new type instead
error: aborting due to previous error
For more information about this error, try `rustc --explain E0117`.
error: could not compile `async-std`.
To learn more, run the command again with --verbose. |
Oh, ugh nevermind. Figured it out; needed to do use async_std::io::{self, BufReader, BufWriter};
use async_std::net::TcpStream;
use async_std::sync::Arc;
use async_std::task;
fn main() -> io::Result<()> {
task::block_on(async {
let addr = "localhost:8080";
let conn = Arc::new(TcpStream::connect(&addr).await?);
let reader = BufReader::new(&*conn.clone());
let writer = BufWriter::new(&*conn);
Ok(())
})
} |
Indeed, it seems coherence rules don't allow the impl for @yoshuawuyts |
Perhaps we should implement let conn = TcpStream::connect(&addr).await?;
let reader = BufReader::new(conn.clone());
let writer = BufWriter::new(conn); In the standard library we have I believe we could've also had a regular However, in |
This has been implemented; only thing left is stabilization now but we should track that separately. |
Does this also close #553? Edit: Oops, I forgot that was split off from the original ask. Disregard. |
The current signature of
async_std::io::copy
is:Both reader and writer need to be mutable references because we're just mimicking
std::io::copy
.Unfortunately, this API is annoying to use when we want to share
TcpStream
s. Just look at this convoluted&mut (&stream, &stream)
pattern:I think we can do better. What if we didn't follow the API from
std
and had the following instead?This might look like a less powerful APIs than the previous one, but I believe it is functionally the same. Note that we have these blanket impls of
Read
andWrite
:That means if we can pass
&mut T
into the previous API, then it should be totally fine to pass it into the new one too! In other words, the new API is fully compatible with the previous one (unless I'm missing something here).So the cool thing is that while it might seem we're deviating from the
std
APIs, we kind of aren't. :)Here's how we can write an echo TCP server using the new
async_std::io::copy
:And here's how we do it on
Arc<TcpStream>
:This change could make sharing streams a lot easier, and we've seen plenty of people struggle with this problem. Wdyt?
The text was updated successfully, but these errors were encountered: