While studying async_std
I have come across the following idiom in Rust
let (reader, writer) = &mut (&stream, &stream);
Which makes both reader
and writer
mutable pointers to stream
. How does it work?
Doesn't Rust stop you from having two or more mutable pointers?
Which makes both
reader
andwriter
mutable pointers tostream
.
It doesn't. This is a mutable reference to a tuple which contains immutable references to stream
. There is no possibility of using this to mutate the original stream
, all you can do is mutate the tuple by swapping a reference to a completely different stream.
You can use methods of Read
and Write
on these because &TcpStream
implements Read
and Write
. In that expression, the type of reader
and writer
is &mut &TcpStream
, not &mut TcpStream
.
Note that Read
and Write
are not implemented for references to all types that implement them. For example Write
is implemented for Cursor<Vec<u8>>
but it is not implemented for &Cursor<Vec<u8>>
. This implementation would be unsound because it would allow the same bit of memory to be accessed via two different mutable references.
The trait methods mostly take &mut self
so that if it is unsafe to have multiple readers or writers on the same reader or writer, it can be implemented soundly and the Rust type system will take care of making sure that there is only one. But this would be too restrictive for TcpStream
, since it's ok to have multiple readers and writers.
Rather than duplicating code by adding another more permissive trait async_std
implements Read
and Write
for immutable references in cases where it is sound to do so. The pattern you've seen is a kind of "trick" that makes this design work.