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>.
Related
The following code gives me the "Assignment to borrowed a" error. Hows the compiler able to know that? Is the compiler special casing RefCell or is there something in the language that allows it to tell the compiler you have a borrowed value?
use std::cell::RefCell;
fn main() {
let mut a = RefCell::new(A{a:5});
let mut b = a.borrow_mut();
a = RefCell::new(A{a:6});
}
Also, why does this code work which seems to be doing the exact same thing?
use std::cell::RefCell;
fn main() {
let mut a = Box::new(A{a:5});
let mut b = &mut a;
a = Box::new(A{a:6});
}
The compiler is not special casing RefCell. But borrow_mut() has the following signature:
pub fn borrow_mut(&self) -> RefMut<'_, T>
So it returns a RefMut that keeps the RefCell borrowed while it is alive (because of the '_, that according to the lifetime elision rules borrows from self). So while it is alive, you cannot assign to the RefCell because it is borrowed.
The reason the second case with a mutable reference works is that since mutable references don't implement Drop (a more precise term is that they don't have a drop glue, that is, neither they implement Drop nor any of their fields (which is none for mutable references) has a drop glue, recursively), the compiler shortens the borrow and drop the reference early. But it cannot do that with the RefMut from the RefCell because it implements Drop, and thus early-dropping it would change program behavior.
I wanted to know if rust is doing variable shadowing under the hood when variables move from one scope to another.
Since when I move a variable to another function the other function's signature can be changed to make the variable mutable and but when the variable is moved/returned back from other function to main it's not immutable anymore.
I am just curious to what's happening here.
fn main() {
let vec0 = Vec::new(); // immutable by default
let mut vec1 = foreign_func(vec0); // vec0 has now moved
// Is foreign_func returning a mutable vector? Because without `mut` keyword vec1 is immutable by default.
vec1.push(10);
}
fn foreign_func(mut vec: Vec<i32>) -> Vec<i32> {
// argument passed for vec is now mutable in this scope.
// Is this like variable shadowing?
// let test = 10;
// let mut test = 20;
vec.push(20);
vec
}
There's no such thing as an immutable Vec, it's only a variable or a borrow that can be immutable (actually unique/exclusive, but in case of Vec that's the same thing).
What happens in your example is a move: the Vec first moves from main's vec0 into foreign_func's vec, and then back to main, this time to vec1. Some of these bindings are mut and some are not, but that has no bearing on the moved value. In fact, transferring a Vec from an immutable to a mutable variable can be as simple as let mut vec1 = vec0, which compiles just fine.
As pointed out by Peter Hall, shadowing is a different concept: introducing a new variable which intentionally shares the name of an existing one. Since the two variables have the same name, only the most recent one is accessible in its scope. Note that the shadowed variable and the one that shadowed it don't have to have the same type, and that a move may or may not occur when shadowing:
fn shadow1(v: Option<Vec<u32>>) {
// here shadowing moves the old value
let v = v.unwrap_or_else(Vec::new);
}
fn shadow2(v: Vec<u32>) {
// here shadowing not accompanied by move
let v = v.as_slice();
}
When there is no move, it is possible that the shadowed value will again become useful after an operation ends. In that case the shadowing variable may be introduced in a separate scope, so that the old value resurfaces after the inner scope finishes:
fn shadow3(v: Vec<u32>) {
{
let v = v.as_slice();
// temporarily working with a &[u32] slice, Vec is inaccessible
}
// here we have the Vec again
}
First of all, I'm not asking what's the difference between &mut and ref mut per se.
I'm asking because I thought:
let ref mut a = MyStruct
is the same as
let a = &mut MyStruct
Consider returning a trait object from a function. You can return a Box<Trait> or a &Trait. If you want to have mutable access to its methods, is it possible to return &mut Trait?
Given this example:
trait Hello {
fn hello(&mut self);
}
struct English;
struct Spanish;
impl Hello for English {
fn hello(&mut self) {
println!("Hello!");
}
}
impl Hello for Spanish {
fn hello(&mut self) {
println!("Hola!");
}
}
The method receives a mutable reference for demonstration purposes.
This won't compile:
fn make_hello<'a>() -> &'a mut Hello {
&mut English
}
nor this:
fn make_hello<'a>() -> &'a mut Hello {
let b = &mut English;
b
}
But this will compile and work:
fn make_hello<'a>() -> &'a mut Hello {
let ref mut b = English;
b
}
My theory
This example will work out of the box with immutable references (not necessary to assign it to a variable, just return &English) but not with mutable references. I think this is due to the rule that there can be only one mutable reference or as many immutable as you want.
In the case of immutable references, you are creating an object and borrowing it as a return expression; its reference won't die because it's being borrowed.
In the case of mutable references, if you try to create an object and borrow it mutably as a return expression you have two mutable references (the created object and its mutable reference). Since you cannot have two mutable references to the same object it won't perform the second, hence the variable won't live long enough. I think that when you write let mut ref b = English and return b you are moving the mutable reference because it was captured by a pattern.
All of the above is a poor attempt to explain to myself why it works, but I don't have the fundamentals to prove it.
Why does this happen?
I've also cross-posted this question to Reddit.
This is a bug. My original analysis below completely ignored the fact that it was returning a mutable reference. The bits about promotion only make sense in the context of immutable values.
This is allowable due to a nuance of the rules governing temporaries (emphasis mine):
When using an rvalue in most lvalue contexts, a temporary unnamed lvalue is created and used instead, if not promoted to 'static.
The reference continues:
Promotion of an rvalue expression to a 'static slot occurs when the expression could be written in a constant, borrowed, and dereferencing that borrow where the expression was the originally written, without changing the runtime behavior. That is, the promoted expression can be evaluated at compile-time and the resulting value does not contain interior mutability or destructors (these properties are determined based on the value where possible, e.g. &None always has the type &'static Option<_>, as it contains nothing disallowed).
Your third case can be rewritten as this to "prove" that the 'static promotion is occurring:
fn make_hello_3<'a>() -> &'a mut Hello {
let ref mut b = English;
let c: &'static mut Hello = b;
c
}
As for why ref mut allows this and &mut doesn't, my best guess is that the 'static promotion is on a best-effort basis and &mut just isn't caught by whatever checks are present. You could probably look for or file an issue describing the situation.
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.
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