This question already has an answer here:
Do mutable references have move semantics?
(1 answer)
Closed 1 year ago.
here's my problem:
fn main() {
let mut s = String::from("hello");
let s1 = &mut s;
let s2 = s1;
*s2 = String::from("world1");
*s1 = String::from("world2");
println!("{:?}", s);
}
it will result in a compile error because s1 has type &mut String which doesn't implement the Copy trait.
But if I change the code as below:
fn c(s: &mut String) -> &mut String {
s
}
fn main() {
let mut s = String::from("hello");
let s1 = &mut s;
let s2 = c(s1);
*s2 = String::from("world1");
*s1 = String::from("world2");
println!("{:?}", s);
}
it will compile without any error message.
I know when a reference passed to a function, it means the reference borrows the value insteading of owning it.
But in the situation above, it seems like when s1 was passed to fn c and returned immediatelly, s2 borrowed s1 so s1 couldn't be derefed until s2 was out of it's lifetime scope.
So what happened when s1 was passed into the fn c?
From #Denys Séguret's hint, I guess when s1 was passed to fn C, Rust core compiled the parameter s1 to something like &mut *s1, so there was an immutable borrow of s1.
That's why if we put
*s2 = String::from("world1");
behind
*s1 = String::from("world2");
Rust would tell us:
assignment to borrowed `*s1`
And when s2 goes out of it's lifetime scope, there is no borrow of s1 anymore, so s1 can be derefed again.
But I'm not quite sure whether it's a right explanation.
Related
I created two example codes, one using references of a Box and the other using borrow_mut of a RefCell and they don't work the same, to my surprise.
use std::cell::RefCell;
#[derive(Debug)]
struct A {
a: usize,
}
fn main() {
let m;
let mut n = Box::new(A{a:5});
{let a = &mut n; m = &mut a.a;}
*m = 6;
println!("{}", m);
println!("{:?}", n);
let mm;
let mut nn = RefCell::new(A{a:5});
{let mut aa = nn.borrow_mut(); mm = &mut aa.a;}
*mm = 6;
println!("{:?}", mm);
println!("{:?}", nn);
}
I'm getting an error saying the aa borrowed value does not live long enough. &mut aa.a should be &mut usize so nothing to do with a RefCell and should work just like the Box example. Why am I getting this error?
The problem is that while the borrow &mut n is just a pure reference, nn.borrow_mut() is not. It returns a RefMut that implements Drop - because it needs to mark the RefCell as no longer borrowed after it is dropped. Therefore, the compiler can extend the lifetime of &mut n as it doesn't do anything when dropped, but it cannot do so for nn.borrow_mut() because it alters the behavior of the program.
I'm reading through chapter 4 of the Rust book, coming from a Python background.
What confuses me a bit is that this doesn't compile:
fn do_something(s3: String) {
println!("{}", s3);
}
fn main() {
let s = String::from("Hello");
do_something(s);
println!("{}", s);
}
(as it shouldn't, because s is moved in the function call), but this does:
fn do_something(s3: &String) {
println!("{}", s3);
}
fn main() {
let s1 = String::from("Hello");
let s2 = &s1;
do_something(s2);
println!("{}", s2);
}
if I understand correctly, this is because s1 is already a pointer, so s2 is a pointer (well, a refrence) to a (stack-allocated) pointer. Therefore, s2 is copied in the function call and not moved. Is this so? Is the above the same as this?
fn main() {
let a = 5;
let b = &a;
do_something(b);
println!("{}", b);
}
fn main() {
let s1 = String::from("Hello");
let s2 = &s1;
do_something(s2);
println!("{}", s2);
}
This works because references are Copy. s2 is copied, not moved, and can continue to be used after the call to do_something.
Is the above the same as this?
fn main() {
let a = 5;
let b = &a;
do_something(b);
println!("{}", b);
}
Yes, it is. It doesn't matter if a or s1 are Copy, only that b or s2 are.
I guess my question is, are they Copy because they are always pointers to stack-allocated data?
All shared references are Copy because of the blanket implementation:
impl<'_, T> Copy for &'_ T
where
T: ?Sized,
It doesn't matter where the references point, stack or heap or .data section or anywhere else. References are small word-sized values and it's convenient and efficient to copy them, so Rust makes them Copy.
That's the usual metric: Is copying efficient? Strings aren't Copy because cloning them is an expensive O(n) operation. &strs, being references, are Copy because copying pointers is cheap.
(It's not the only metric, of course. Some types are not Copy because they are logically unique. For example, Box is also just a pointer, but it's an owned type, not a shared reference.)
I am noob to Rust and wanted to solidify my understanding of mutable reference in Rust.
fn mutate_me(st: &mut String)-> usize {
st.push_str(" mutated.");
st.len()
}
When I try using any one of either x or mutable reference r1 there is no compilation error.
fn main() {
let mut x = String::from("random");
let r1 = &mut x;
println!("{}", x);
}
This also work without any compilation error.
fn main() {
let mut x = String::from("random");
let r1 = &mut x;
println!("{}", mutate_me(r1));
}
But the one below fails as I tried using both of them.
fn main() {
let mut x = String::from("random");
let r1 = &mut x;
println!("{}", mutate_me(r1));
println!("{}", x);
println!("{}", *r1);
}
Does this imply that for mutable reference, whichever tried using it next first becomes valid and the other one invalid?
As a complement to #ChayimFriedman answer. Usually (not always) you have to work around the borrowing and flow of your program using scopes, so that things are used and dropped to liberate outer ones. For example your third case could be transformed as follows:
fn main() {
let mut x = String::from("random");
let res = {
let r1 = &mut x;
println!("{}", r1);
mutate_me(r1)
};
println!("{}", res);
println!("{}", x);
}
Playground
From the moment you borrow something to the last usage of the reference, the reference is considered alive for borrowing. Accessing an object while a mutable reference to it is alive is an error.
In your first example, you don't use r1 (it is immediately dropped), so the region of code where you can use x is between the two statements let r1 = &mut x; and let r1 = &mut x;, meaning... zero code.
In the second example you never use x, so in the code region x is invalid to use - between the declaration of r1 and its use - x is never actually used. Everything is fine.
The problem is in the third example, when you use x in the "invalid range". You can remove the println!("{}", mutate_me(r1)); - it doesn't matter.
The compiler points on these three important points: where the reference was created, when it's being used later, and where you perform the invalid access (this is called the "three-points models" in the NLL RFC):
error[E0502]: cannot borrow `x` as immutable because it is also borrowed as mutable
--> src/main.rs:4:20
|
3 | let r1 = &mut x;
| ------ mutable borrow occurs here
4 | println!("{}", x);
| ^ immutable borrow occurs here
5 | println!("{}", *r1);
| --- mutable borrow later used here
Playground.
I have a function that takes the mutable reference of the string and appends some text.
fn append_str(s: &mut String) {
s.push_str(" hi");
}
Suppose I have a string.
let mut s: String = "hi".to_string();
If I create the mutable reference to s and pass it to append_str, it compiles without a problem.
let mut ss = &mut s;
append_str(&mut ss);
However, if I expliclty define ss with &mut String, it does not compile.
let ss: &mut String = &mut s;
append_str(&mut ss);
it shows following compiler error.
|
80 | let ss: &mut String = &mut s;
| -- help: consider changing this to be mutable: `mut ss`
81 | append_str(&mut ss);
| ^^^^^^^ cannot borrow as mutable
One thing funny is that if I dereference it, then it works.
let ss: &mut String = &mut s;
append_str(&mut *ss); // OK
What is the reason that we have to explicitly dereference in this case?
One more question: Why do we have to specify mut to the reference if we want to pass it to the function?
let ss = &mut s;
append_str(&mut ss); // ERROR
ss is a reference already, so &mut ss gives you (mutable) reference to (mutable) reference; if you have ss, you should call append_str with it directly: append_str(ss).
It is only when you incorrectly take a mutable reference to ss, you need to declare it as mut ss. The normal use case for something like that is to pass it to a function that actually accepts x: &mut &mut String and uses something like *x = &mut some_other_string to make ss refer to a different reference to string. In your case the "fixed" code with mut compiles because the compiler automatically dereferences the double-reference for you.
Why does the compiler reject this code:
struct S<'a> {
i: i32,
r: &'a i32,
}
fn main() {
let mut s = S{i: 0, r: &0};
{
let m1 = &mut s;
m1.r = &m1.i;
}
let m2 = &mut s;
}
The error is: "cannot borrow s as mutable more than once at a time" (first borrow: m1, second borrow: m2).
Why is the first borrow of s still alive after m1 goes out of scope?
I read about borrow scope extension beyond the scope of the original borrower. However, this always seemed to involve another borrower outside the scope of the original borrower that "took over" the original borrow, e.g. this code fails with the exact same error, which is clear to me:
fn main() {
let mut s = 0;
let r: &mut i32;
{
let m1 = &mut s;
r = m1;
}
let m2 = &mut s;
}
In the first example, if I replace m1.r = &m1.i; with m1.r = &dummy; (dummy defined as some &i32) or with let dummy = &m1.i;, the code compiles. The error occurs only if I store a reference to a field in another field of the borrowed struct. I don't see why this should extend the borrow beyond its scope.
My best guess as to what is wrong with the code is:
s.r's original lifetime is the whole of main,
when I assign a reference to m1.r it has to be that original lifetime, but &m1.i is only valid for as long as m1 lives.
But I might be wrong (the error message would be misleading then).
First note that
let mut s = S{i: 0, r: &0};
{
s.r = &s.i;
}
let m2 = &mut s;
Gives
cannot borrow `s` as mutable because `s.i` is also borrowed as immutable
Hopefully this should be clear - if a struct self-borrows then it is borrowed. This points out why any self-borrowing structure is basically useless - it cannot be moved (invalidating its own pointer) nor can and mutable reference be taken to it.
Next one needs to understand that immutable references from mutable references count as borrows into the mutable reference, so extend it. For example
let mut v = ();
let r1 = &(&mut v);
let r2 = &v;
gives
cannot borrow `v` as immutable because it is also borrowed as mutable
It's not clear if this is legally able to be a new borrow into the original structure, but it as-yet does not act as such.