My first point of confusion is why does the stream parameter need to be declared as
mut stream: &TcpStream
rather than
stream: &mut TcpStream
In the Rust Book chapter on References and Borrowing, a string is passed as some_string: &mut String.
My understanding on the different here is that the first one is a "mutable reference to a TcpStream" (meaning we can modify the reference but not the TcpStream it refers to), and the second is a "reference to a mutable TcpStream" (which means we can change the TcpStream but not make the reference refer to something else). Is that correct? Or am I thinking about this wrong?
Out of curiosity, I tried changing the stream parameter to stream: mut TcpStream and found that this is not a thing. It fails to compile.
Another thing that puzzles me: this code calls read() on the stream which takes &mut self, which seems like it would require stream: &mut TcpStream but handle_connection() has mut stream: &TcpStream. Is &mut self special in this regard?
This is a strange quirk of the stdlib IO types - both TcpStreamand&TcpStream implement Read! This allows you to perform reads while not having mutable access to the TcpStream type itself, but it's a somewhat obscure feature.
Worth noting here is the reason why &TcpStream is allowed to do this:
In rust, when &T implements some trait that has &mut self methods, it means that the implementation is thread-safe. (or more specifically, that it cannot create data races in safe code)
So normally a call to read() would require &mut T, but most (all?) stdlib IO types will work with &T as well?
Why is a mutable reference required at all? The code does not compile with stream: &TcpStream. The read() method uses &mut self, but mutability is not actually required.
You have to implement methods for T and &T separately? Is this only true for trait methods? Or in general?
How do you indicate in source that you are implementing a trait for &T instead of T? I don't see any indication in stdlib tcp.rs that it's done for both.
You need a mut in mut stream: &TcpStream so that the read() method can borrow mutably. The actual type of self in that call becomes &mut &TcpStream.
For a concrete T (or a trait definition), you can decide whether methods on it take it by value, immutable (aka shared) borrow, or mutable (aka unique) borrow. This is done by declaring self, &self or &mut self, respectively, in the method signature.
The difference is:
// impl for a value
impl SomeTrait for MyType { ... }
// impl for immutable reference
impl<'a> SomeTrait for &'a MyType { ... }
Thanks everyone. Your replies have been very helpful.
Bringing it all together, I've got a simple program for anyone interested that shows this behavior.
It really clicked for me when I realized that implementing a trait for &T means you cannot have a mutable T in those trait methods. If you are implementing the trait for &T, then self is &T, and &mut self is &mut &T
You can demonstrate this by trying to modify self in the implementation for &MyNum which produces the error
18 | self.0 += 1;
| ^^^^^^^^^^^ cannot mutably borrow field of immutable binding