Why does std::io::stdin().read_line expect a &mut string?


#1

Total beginner here: I can understand it accepting a &string, as read_line should mutate the variable’s data, but why would it need to change where the variable is pointing to?


#2

It’s not; &mut String is a reference to a mutable string; mut &String would be a mutable reference to an immutable string.


#3

interesting. I was going through the tutorial and the “guess” variable was already stated to be mutable. Why is it necessary to do so again?


#4

The String is owned by its binding: guess. & and &mut let you borrow that String.

&guess is an immutable reference. It gives you a “read access” but that’s all.
&mut guess is a mutable reference. It lets you mutate the object owned by the binding guess.

You may want to lookup the chapter 4 of the book on Ownership.


#5

How mutability works in Rust is central to its design, so I guess you will see more of it and understand it in its context as you go along.

In brief, mutable access means exclusive access, and even if you have a mutable variable for a string, you might not want to give a particular function you call exclusive access to it – not if you need something else to read from the same variable concurrently, for example.

Rust has already chosen to make passing references explicit (must use & / &mut syntax to pass a reference to a variable), and which kind of reference is also explicit — maybe because it matters for the borrow checker (the part of the compiler that checks the rules for references).


#6

You can replace a &mut reference using std::mem::replace, which is safe.


#7

Others have already said this, but the mut modifier is for the owner of the value - it allows them to mutate the value. They can also lend it out immutably to other places, but those cannot modify it. They can also lend it out mutably, which allows modification by a single possessor of the mutable borrow. If the owner doesn’t declare the value as mutable to begin with, no mutable borrows are allowed either. So ownership and mutability are front and center in Rust, it’s very explicit about them.


#8

And replace is a safe wrapper around unsafe code, which I specifically said could do that. My point was that references should be thought of as borrowings, not as pointers, although they are.


#9

Ok fair enough :thumbsup:


#10

Sorry, but that’s misleading. With that argument all of std is a safe wrapper around unsafe code somewhere.

Fact is, mem::replace is a safe standard function and there is nothing at all unsafe about using it to replace the object referenced by a &mut reference.


#11

That’s how I looked at it initially as well, but technically it’s right - it requires unsafe code internally; if no such function existed, you’d implement it via unsafe since it’s not expressible in the language itself.


#12

It’s just the swapping out of the old value that’s not expressible without the function. If you just want to replace the referenced object, *ref = newvalue does not need a function.


#13

That’s true. For some reason I thought we were talking about replacement in the mem::replace sense, but maybe I read too much into it.


#14

I guess we come from different perspectives. In Rust I find you’re often not supposed to think in terms of pointers. I meant to provide a proper thought model and have it contrast with the way C and C++ do things. Sort of a “pedagogy comes before technically-correct” if you will.


#15

That’s a good perspective to have, but with respect to mutable references, I’d leave the comparison to raw pointers in C/C++ at “guaranteed non-null and guaranteed to be unique (i.e. no other read/write aliasing)” (with “guaranteed” being modulo nobody doing unsafe things). That alone is quite powerful. The replacement portion is confusing and possibly inaccurate, depending on how one interprets it (as witnessed in this thread).


#16

Yeah, fair enough. I’ve edited my answer not to mislead anyone.