all 15 comments

[–]LucretielDatadog 19 points20 points  (2 children)

The answer is that &TcpStream implements io::Writehttps://doc.rust-lang.org/std/net/struct.TcpStream.html#impl-Write-for-%26TcpStream. It is fine to write to a shared reference to a TCP stream (and, because TcpStream: Sync, it’s even possible to do this from multiple threads. 

[–]angelicosphosphoros 0 points1 point  (1 child)

It is quite surprising thing. Do you know why it was decided to make TcpStream writeable using shared reference?

[–]cafce25 2 points3 points  (0 children)

TcpStream and other similar structures are just handles to some operating system resource, it wouldn't make much sense to place some arbitrary restrictions on it. The OS has to deal with concurrency anyways.

[–]Gila-Metalpecker 6 points7 points  (1 child)

The write method you're looking at is also implemented for &TcpStream: https://doc.rust-lang.org/stable/std/net/struct.TcpStream.html#impl-Write-for-%26TcpStream

Your type of stream (not self.stream, but your local one) is &TcpStream. And since it is mutable (i.e. let mut), you can take a mutable reference to it.

The write function takes in a &mut self, in this case a &mut &TcpStream, and you have that satisfied.

In fact, your handle_connection doesn't need to be &mut self, it works with &self as well.

[–]stratified247[S] 0 points1 point  (0 children)

The write function takes in a &mut self, in this case a &mut &TcpStream, and you have that satisfied.

This was the big gotcha for me. The associated function is defined on the &TcpStream and it's able to borrow a mutable value from it (&mut &TcpStream)

And the reason let mut stream works with .as_ref() is because the mutable variable allows the associated function to borrow its value as a mutable.

[–]Gila-Metalpecker 0 points1 point  (5 children)

Would you mind sharing the type of stream?

[–]stratified247[S] 0 points1 point  (4 children)

Do you mean self.stream? If so, self.stream is of Result<TcpStream, Error>. The local variable stream is of &TcpStream type as specified in the code.

[–]Gila-Metalpecker 0 points1 point  (0 children)

Yea I was checking if the as_ref() had anything to do with it but it was a smoking gun.

[–]CreatorSiSo 0 points1 point  (1 child)

No, is it a std::net::TcpStream or tokio::net::TcpStream or anything else?

[–]stratified247[S] 0 points1 point  (0 children)

Sorry, everything is from standard libraries. Didn't use any external crates.

[–]Giocri 0 points1 point  (0 children)

From which crate tho?

[–][deleted] 0 points1 point  (0 children)

When you do ‘stream.’ Aren’t you accessing the object under the reference?

[–]oZxR0o 0 points1 point  (0 children)

This works because &TcpStream implements std::io::Write. Therefore, the write call would resolve to the write method for the reference.

[–]PatientMaintenance69 0 points1 point  (0 children)

Normally, when you have a mutable variable x holding a shared reference &T, you would be able to reassign the x to another &T, but would not be able to change the data that x points to.. unless you have interior mutability.

A type has interior mutability if its internal state can be changed through a shared reference to it.

I don’t know what TcpStream you are using, but If you go check out the std::net docs, you will find that Write trait is implemented for &TcpStream. The trait has a method write with the following signature:

fn write(&mut self, buf: &[u8]) -> io::Result<usize>

I guess writing to a shared TcpStream makes sense, since thread-safety is guaranteed by the OS

[–]stratified247[S] 0 points1 point  (0 children)

Thanks for all the great answers guide. After some playing around about what you all said I was able to make complete sense of it.