You are close, but you are missing one key detail.
In Rust, assignment (including that of a function parameter) happens by moving the value, unless the value's type implements Copy
, in which case it is copied. In C++ you have to request a move with std::move()
(in the cases where it's not implicit), whereas in Rust, moving is automatic for values that cannot be copied, and a move vacates the original variable such that trying to read from it again results in a compile-time error.
The Copy
trait is implemented for all shared references (&T
) but it is not implemented for mutable references (&mut T
).
However, the compiler does a sneaky thing in this particular case. If the the reference were moved, then s
would be in a "moved from" state after the call to func()
and so you would expect that you couldn't use it anymore... but you can!
func(s);
func(s);
This compiles. So what's going on?
The compiler inserts a reborrow here, as though you had written this:
func(&mut *s);
This kind of construct asks the compiler not to consume the reference s
and instead reborrow its referent. (This happens implicitly when passing references to functions, as is happening here, but there are other cases where it's not implicit and you have to explicitly reborrow.)
But if so, there're two mutable references for the same variable word
, violating the defined rule of Rust.
Ah, but that's not quite the rule. The rule is that, at any given moment, a value can be either:
- Readable by any number of names, or
- Writable by at most one name.
This doesn't mean that you can't have two mutable references to the same value, it just means that only one can be usable at a time.
In your code, main()
transfers control to func()
after reborrowing s
. This means that s
cannot be used until s_ref
goes away -- but it could not possibly be used until s_ref
goes away, because func()
has to return before s
could be used again.
So as you can see, this doesn't violate Rust's aliasing rules. When you call func()
, the value word
can be written to using s_ref
, but it cannot be written to using s
until the function returns. Then s
is usable again.
We can demonstrate this with the following two programs:
fn func(s_ref: &mut str) {}
fn main() {
let mut word = "hello".to_string();
let s: &mut str = &mut word;
let s2: &mut str = &mut *s;
func(s2);
func(s);
}
This compiles! We reborrow s
into s2
, but we don't use s
until we stop using s2
. The compiler figures this out by itself (see non-lexical lifetimes).
However, if we interleave the usages then we have a problem:
fn func(s_ref: &mut str) {}
fn main() {
let mut word = "hello".to_string();
let s: &mut str = &mut word;
let s2: &mut str = &mut *s;
func(s);
func(s2);
}
Now the compiler will complain:
error[E0499]: cannot borrow `*s` as mutable more than once at a time
--> src/main.rs:8:10
|
6 | let s2: &mut str = &mut *s;
| ------- first mutable borrow occurs here
7 |
8 | func(s);
| ^ second mutable borrow occurs here
9 | func(s2);
| -- first borrow later used here
Between lines 6 and 9, s
and s2
are both considered to be usable, and this is disallowed.
As you can see, there is no problem having multiple mutable references to the same value. However, it's not permitted for their usage to overlap. As long as they don't overlap, there isn't an issue.