If I get correctly it is not possible to create a mutable borrow over a std::rc::Rc in Rust, you have to use Cell or RefCell.
But anyway I cannot understand how to use them. For example consider this simple example:
use std::cell::RefCell;
struct X (i32);
impl X {
fn foo(&mut self) {
self.0 = 0;
}
}
fn main () {
let x = X(5);
let rcx = RefCell::new(&x);
let mut mutx: std::cell::RefMut<&X> = rcx.borrow_mut();
(*mutx).foo();
}
I get the following error:
16:5: 16:9 error: cannot borrow immutable local variable `mutx` as mutable
16 mutx.foo();
But if I remove the reference from line (and update type of mutx):
let rcx = RefCell::new(x);
Everything is fine. But I cannot understand why, since RefMut::deref_mut() -> &mut T the deference called at line 16 should return &&mut T in the first case, while &mut T in the second case. But since the compiler should apply many * as needed (If I get how deref coercion works) there should be no difference between RefMut<X>::deref_mut() and RefMut<&X>::deref_mut()
Edit:
By mistake I forgot to write mut at line 15 as in the linked example is correctly is written. So now it's let mut mutx...
The problem stems from the fact that you've stored an immutable reference in the RefCell. I'm unclear why you would want such a thing. The normal pattern is to put the entire value into the RefCell, not just a reference:
fn main () {
let rcx = RefCell::new(X(5));
let mut mutx = rcx.borrow_mut();
mutx.foo();
}
Problem from original question
You have two compounding errors. Let's check the entire error message:
<anon>:16:5: 16:12 error: cannot borrow immutable borrowed content as mutable
<anon>:16 (*mutx).foo();
^~~~~~~
<anon>:16:7: 16:11 error: cannot borrow immutable local variable `mutx` as mutable
<anon>:16 (*mutx).foo();
^~~~
Note the second error — "cannot borrow immutable local variable mutx". That's because you need to declare the mutx variable mutable:
let mut mutx: std::cell::RefMut<&X> = rcx.borrow_mut();
That will allow mutx to participate in DerefMut.
Related
The following two rust functions are identical other than the fact that one returns an immutable reference and the other returns a mutable one. Since neither involves borrowing something multiple times, I don't see why the two sould work any differently. However, the one with the mutable reference results in a compile error, while the one with the immutable reference does not:
// This complies with no problems
fn foo<'a>() {
let _: &'a () = &();
}
// This does not compile (see error below)
fn foo_mut<'a>() {
let _: &'a mut () = &mut ();
}
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:14:30
|
13 | fn foo_mut<'a>() {
| -- lifetime `'a` defined here
14 | let _: &'a mut () = &mut ();
| ---------- ^^ creates a temporary which is freed while still in use
| |
| type annotation requires that borrow lasts for `'a`
15 | }
| - temporary value is freed at the end of this statement
For more information about this error, try `rustc --explain E0716`.
error: could not compile `playground` due to previous error
It is also possibly relevant that when there are no explicit lifetimes, the code also has no problem compiling:
// This also compiles with no problem
fn foo_mut_without_lifetime() {
let _: &mut () = &mut ();
}
It seems the only thing that is causing a problem is trying to store a mutable reference with a lifetime, and that immutable references and references without explicit lifetimes have no issue. Why is this happening, and how can I get around it?
Note that there's nothing special about () or generic lifetimes here. This compiles fine:
fn allowed() -> &'static i32 {
let x = &3;
let y: &'static i32 = x;
y
}
And this does not:
fn not_allowed() -> &'static mut i32 {
let x = &mut 3;
let y: &'static mut i32 = x;
y
}
So why is the immutable reference allowed?
When you take a reference of a value, Rust infers the lifetime based on where the value's going to die. Here's an example:
let y;
{
let x = 3;
y = &x;
println!("{y}"); // works fine, `y` is still alive
} // `x` will get dropped at the end of this block
println!("{y}"); // fails to compile, the lifetime of `y` has expired (since `x` has died)
Since x dies at the end of the block, Rust knows that the lifetime of the y reference should only extend until the end of the block as well. Hence, it stops you from using it after x is dead.
This seems pretty obvious. But take a moment to think. In the following code:
let x;
{ // block A
x = &3;
}
What is the inferred lifetime of x? You may be tempted to say "the same as block A". But this would in fact be incorrect. Why? Because Rust is a bit smarter than that. It knows that 3 is a constant, and therefore Rust can fit 3 into the constant table of the final executable. And since the constant table will last as long as the lifetime of the final program, Rust can infer that the expression &3 has a 'static lifetime. Then everything works out fine, since &'static can be cast to any other lifetime as required!
Rust draws an explicit line between constants and temporaries, and one of the benefits of having a constant expression is that taking an immutable reference of any constant will always yield a 'static lifetime. This is not true of temporaries. The following code will not compile:
fn f() -> &'static String {
let x = &String::new();
let y: &'static String = x;
y
}
This is because for temporaries, Rust can't put them in the constant table of the executable, since they have to be computed on-demand, and therefore share the same lifetime as the scope they're in.
Okay, this is great, but why isn't the mutable reference of a constant allowed to be 'static?
There are two problems with allowing this:
On some architectures, constant tables can't be modified. This is true of WASM and some embedded architectures, as well as all Harvard-architecture machines. Providing a &mut reference would just be complete nonsense, since they're not mutable. And such fundamental borrow checker rules should really not differ between platforms.
A &'static mut reference is dangerous, since it is quite literally a global variable.
I am trying to understand what exactly happens when functions reborrow mutable references.
fn main() {
let mut a = String::new();
let mut b = String::new();
let aa = &mut a;
let c = my_fun(aa, &mut b);
let d = &mut a;
println!("{}", c);
}
fn my_fun<'b>(x: &'b mut String, y: &'b mut String) -> &'b mut String { y }
From my understanding the my_fun reborrows aa as &*aa whose scope would be the my_fun. But due to the lifetime bound I created in the function signature the reborrow should live at least as long as &mut b exist. So the println force the reborrow to live until it.
Shouldn't this be throwing an error of use after free because the anonymous reborrow has only scope inside my_fun? Outside of this function this reference shouldn't be valid.
But the error I get is:
error[E0499]: cannot borrow `a` as mutable more than once at a time
--> src/main.rs:7:13
|
5 | let aa= &mut a;
| ------ first mutable borrow occurs here
6 | let c = my_fun(aa, &mut b);
7 | let d = &mut a;
| ^^^^^^ second mutable borrow occurs here
8 | println!("{}", c);
| - first borrow later used
which would have made sense if the mutable reference was merely copied instead of reborrowed inside the function.
From my understanding the my_fun reborrows aa as &*aa whose scope would be the my_fun.
It's not quite that.
Let's backtrack a bit: why reborrowing?
There is a fundamental difference between &T and &mut T: &T is Copy, whereas &mut T is not even Clone. The result is that &mut T can only be moved and therefore a call such as:
let x: &mut T = /*...*/;
func(x);
Would result in x being unusable after the call. The work-around would then be to introduce a temporary:
let x: &mut T = /*...*/;
let tmp = &mut *x;
func(tmp);
This temporary would re-borrow the pointee of x, and be consumed by func.
And... that's re-borrowing! The compiler has this "temporary" creation built-in purely for ergonomics!
With that in mind, let's go back to:
From my understanding the my_fun reborrows aa as &*aa whose scope would be the my_fun.
Lifetimes are generally more a range than a point, and this is true here.
The lifetime of tmp in my example above is constrained in 2 ways:
It cannot be greater than that of x.
It cannot be less than that of func.
Which is another way of saying that it can be anything in between those bounds.
I believe you're overthinking "reborrowing" here.
The lifetime requirements you applied say that the thing being referenced by the return value will have at least the lifetime of the things being referenced by the parameters. That's true (and if it weren't provably true, this wouldn't compile). So there is no dangling reference possible.
There isn't a separate "reborrowed" reference. Borrowing is bookkeeping inside the compiler to keep track of lifetimes. There is no let x = &*aa step that actually occurs or is even particularly implied. This isn't like reference counting where memory actually changes at runtime.
Inside of my_fun, y is a reference that's scoped to the function. But the return value is scoped to the caller. (Functions would be impossible to use if this weren't true, having nothing to do with &mut.)
Checkout the Rust code below. It compiles
fn main() {
let vec0 = Vec::new();
let mut vec1 = fill_vec(vec0);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
vec1.push(88);
println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
}
fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
vec.push(22);
vec.push(44);
vec.push(66);
vec
}
Here I am declaring vec0 as immutable but fill_vec takes in a mutable vector. Depending on the function signature it seems Rust is changing the nature of the argument being passed.
My question is, this obviously seems like a "shot yourself in the foot" instant. Why does Rust allow this? Or, is this actually safe and I am missing something?
There are different things at play here that can all explain why this behavior make sense:
First of, mut doesn't really mean "mutable". There are such things as interior mutability, Cells, Mutexes, etc., which allow you to modify state without needing a single mut. Rather, mut means that you can get mutually exclusive references.
Second, mutability is a property of a binding. vec0 in main and vec in fill_vec are different bindings, so they can have different mutability.
See also:
What does 'let x = x' do in Rust?
Finally ownership: fill_vec takes full ownership of its parameter, which effectively doesn't exist anymore in main. Why should the function not be allowed to do whatever it wants with its owned parameters? Had the function taken the parameter as a mutable reference, you would have needed to declare the original binding as mut:
fn main() {
let mut vec0 = Vec::new();
// ^^^ now _needs_ a mutable binding
fill_vec(&mut vec0);
// ^^^^ needs an explicit `&mut` reference
}
fn fill_vec(vec: &mut Vec<i32>) {
// ^^^^ borrows rather than take ownership
// …
}
You're making the argument vec of fill_vec mutable. You are still passing the vec by value.
If you wanted a mutable reference you would have vec: &mut Vec<i32>.
I'm trying to modify the borrow of a mutable value, here is a minimal example:
fn main() {
let mut w: [char; 5] = ['h', 'e', 'l', 'l', 'o'];
let mut wslice: &mut [char] = &mut w;
advance_slice(&mut wslice);
advance_slice(&mut wslice);
}
fn advance_slice(s: &mut &mut [char]) {
let new: &mut [char] = &mut s[1..];
*s = new;
}
the compiler gives me this error:
error[E0623]: lifetime mismatch
--> src/main.rs:10:10
|
8 | fn advance_slice(s: &mut &mut [char]) {
| ----------------
| |
| these two types are declared with different lifetimes...
9 | let new: &mut [char] = &mut s[1..];
10 | *s = new;
| ^^^ ...but data from `s` flows into `s` here
I've tried to give the same lifetime to both borrow, without success.
Also this works if I remove the mutability of w.
This error message is indeed unfortunate, and I don't think it provides a good explanation of what's going on here. The issue is a bit involved.
The error is local to the advance_slice() function, so that's all we need to look at. The type of the slice items is irrelevant, so let's take this function definition:
fn advance_slice_mut<T>(s: &mut &mut [T]) {
let new = &mut s[1..];
*s = new;
}
The first line creates a new slice object starting after the first item of the original slice.
Why is this even allowed? Don't we have two mutable references to the same data now? The original slice *s includes the new slice new, and both allow modifying data. The reason this is legal is that *s is implicitly reborrowed when creating the subslice, and *s cannot be used again for the lifetime of that borrow, so we still have only one active reference to the data in the subslice. The reborrow is scoped to the function advance_slice_mut(), and thus has a shorter lifetime than the original slice, which is the root cause of the error you get – you are effectively trying to assign a slice which only lives until the end of the function to a memory location that lives longer than the function call.
This kind of implicit reborrow happens everytime you call a function that takes an argument by mutable reference, including in the implicit call to index_mut() in &mut s[1..]. Mutable references can't be copied, since this would create two mutable references to the same memory, and the Rust language designers decided that an implicit reborrow is the more ergonomic solution than moving mutable references by default. The reborrow does not happen for shared references, though, since they can be freely copied. This means that &s[1..] will have the same lifetime as the original scope, since it is perfectly fine for two overlapping immutable slices to coexist. This explains why your function definition works fine for immutable slices.
So how do we fix this problem? I believe that what you intend to do is perfectly safe – after reassigning the new slice to the old one, the old one is gone, so we don't have two concurrent mutable references to the same memory. To create a slice with the same lifetime as the original slice in safe code, we need to move the original slice out of the reference we got. We can do this by replacing it with an empty slice:
pub fn advance_slice_mut<T>(s: &mut &mut [T]) {
let slice = std::mem::replace(s, &mut []);
*s = &mut slice[1..];
}
Alternatively, we can also resort to unsafe code:
use std::slice::from_raw_parts_mut;
pub fn advance_slice_mut<T>(s: &mut &mut [T]) {
unsafe {
assert!(!s.is_empty());
*s = from_raw_parts_mut(s.as_mut_ptr().add(1), s.len() - 1);
}
}
What is the difference between passing a value to a function by reference and passing it "by Box":
fn main() {
let mut stack_a = 3;
let mut heap_a = Box::new(3);
foo(&mut stack_a);
println!("{}", stack_a);
let r = foo2(&mut stack_a);
// compile error if the next line is uncommented
// println!("{}", stack_a);
bar(heap_a);
// compile error if the next line is uncommented
// println!("{}", heap_a);
}
fn foo(x: &mut i32) {
*x = 5;
}
fn foo2(x: &mut i32) -> &mut i32 {
*x = 5;
x
}
fn bar(mut x: Box<i32>) {
*x = 5;
}
Why is heap_a moved into the function, but stack_a is not (stack_a is still available in the println! statement after the foo() call)?
The error when uncommenting println!("{}", stack_a);:
error[E0502]: cannot borrow `stack_a` as immutable because it is also borrowed as mutable
--> src/main.rs:10:20
|
8 | let r = foo2(&mut stack_a);
| ------- mutable borrow occurs here
9 | // compile error if the next line is uncommented
10 | println!("{}", stack_a);
| ^^^^^^^ immutable borrow occurs here
...
15 | }
| - mutable borrow ends here
I think this error can be explained by referring to lifetimes. In the case of foo, stack_a (in the main function) is moved to function foo, but the compiler finds that the lifetime of the argument of the function foo, x: &mut i32, ends at end of foo. Hence, it lets us use the variable stack_a in the main function after foo returns. In the case of foo2, stack_a is also moved to the function, but we also return it.
Why doesn't the lifetime of heap_a end at end of bar?
Pass-by-value is always either a copy (if the type involved is “trivial”) or a move (if not). Box<i32> is not copyable because it (or at least one of its data members) implements Drop. This is typically done for some kind of “clean up” code. A Box<i32> is an “owning pointer”. It is the sole owner of what it points to and that's why it “feels responsible” to free the i32's memory in its drop function. Imagine what would happen if you copied a Box<i32>: Now, you would have two Box<i32> instances pointing to the same memory location. This would be bad because this would lead to a double-free error. That's why bar(heap_a) moves the Box<i32> instance into bar(). This way, there is always no more than a single owner of the heap-allocated i32. And this makes managing the memory pretty simple: Whoever owns it, frees it eventually.
The difference to foo(&mut stack_a) is that you don't pass stack_a by value. You just “lend” foo() stack_a in a way that foo() is able to mutate it. What foo() gets is a borrowed pointer. When execution comes back from foo(), stack_a is still there (and possibly modified via foo()). You can think of it as stack_a returned to its owning stack frame because foo() just borrowed it only for a while.
The part that appears to confuse you is that by uncommenting the last line of
let r = foo2(&mut stack_a);
// compile error if uncomment next line
// println!("{}", stack_a);
you don't actually test whether stack_a as been moved. stack_a is still there. The compiler simply does not allow you to access it via its name because you still have a mutably borrowed reference to it: r. This is one of the rules we need for memory safety: There can only be one way of accessing a memory location if we're also allowed to alter it. In this example r is a mutably borrowed reference to stack_a. So, stack_a is still considered mutably borrowed. The only way of accessing it is via the borrowed reference r.
With some additional curly braces we can limit the lifetime of that borrowed reference r:
let mut stack_a = 3;
{
let r = foo2(&mut stack_a);
// println!("{}", stack_a); WOULD BE AN ERROR
println!("{}", *r); // Fine!
} // <-- borrowing ends here, r ceases to exist
// No aliasing anymore => we're allowed to use the name stack_a again
println!("{}", stack_a);
After the closing brace there is again only one way of accessing the memory location: the name stack_a. That's why the compiler lets us use it in println!.
Now you may wonder, how does the compiler know that r actually refers to stack_a? Does it analyze the implementation of foo2 for that? No. There is no need. The function signature of foo2 is sufficient in reaching this conclusion. It's
fn foo2(x: &mut i32) -> &mut i32
which is actually short for
fn foo2<'a>(x: &'a mut i32) -> &'a mut i32
according to the so-called “lifetime elision rules”. The meaning of this signature is: foo2() is a function that takes a borrowed pointer to some i32 and returns a borrowed pointer to an i32 which is the same i32 (or at least a “part” of the original i32) because the the same lifetime parameter is used for the return type. As long as you hold on to that return value (r) the compiler considers stack_a mutably borrowed.
If you're interested in why we need to disallow aliasing and (potential) mutation happening at the same time w.r.t. some memory location, check out Niko's great talk.
When you pass a boxed value, you are moving the value completely. You no longer own it, the thing you passed it to does. It is so for any type that is not Copy (plain old data that can just be memcpy’d, which a heap allocation certainly can’t be). This is how Rust’s ownership model works: each object is owned in exactly one place.
If you wish to mutate the contents of the box, you should pass in a &mut i32 rather than the whole Box<i32>.
Really, Box<T> is only useful for recursive data structures (so that they can be represented rather than being of infinite size) and for the very occasional performance optimisation on large types (which you shouldn’t try doing without measurements).
To get &mut i32 out of a Box<i32>, take a mutable reference to the dereferenced box, i.e. &mut *heap_a.
The difference between passing by reference and "by box" is that, in the reference case ("lend"), the caller is responsible for deallocating the object, but in the box case ("move"), the callee is responsible for deallocating the object.
Therefore, Box<T> is useful for passing objects with responsibility for deallocating, while the reference is useful for passing objects without responsibility for deallocating.
A simple example which demonstrates these ideas:
fn main() {
let mut heap_a = Box::new(3);
foo(&mut *heap_a);
println!("{}", heap_a);
let heap_b = Box::new(3);
bar(heap_b);
// can't use `heap_b`. `heap_b` has been deallocated at the end of `bar`
// println!("{}", heap_b);
} // `heap_a` is destroyed here
fn foo(x: &mut i32) {
*x = 5;
}
fn bar(mut x: Box<i32>) {
*x = 5;
} // heap_b (now `x`) is deallocated here