[SOLVED] Placement of mut in Function Parameters

I'm new to Rust and confused about the placement of mut in function parameters. This is based on code adapted from the Rust Book Final Project

I made the handle_connection function borrow the TcpStream instead of owning it.

Playground

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?

Thanks!

1 Like

Yep, that's exactly what it means.

This is a strange quirk of the stdlib IO types - both TcpStream and &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 { ... }
1 Like

Other types do require the ability to mutate their state.

The implementations are here: https://github.com/rust-lang/rust/blob/master/src/libstd/net/tcp.rs#L592-L605

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
3 Likes