I'm working on the Rust Book (Chapter 4) and I am surprised that code like this compiles:
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{}, {}", r1, r2);
// this line silences the warning: 'variable does not need to be mutable'
s.push_str(" world");
}
Why does Rust allow an immutable reference to a mutable variable? This would seem to weaken the safety guarantees. If I have a mutable variable, and I pass immutable references to some threads, those threads are assuming the value will not change, but I could mutate the value through the original variable.
I haven't reached threading yet, but found this strange, and in this case, no different from C++:
void doNotChangeMyString(const std::string& myConstString) {
// ... assume that myConstString cannot change and use it on a thread
// return immediately even though some worker thread is still
// using myConstString
}
void main() {
std::string s = "hello" // not const!
doNotChangeMyString(s);
s = "world"; // oops
}
Edit: I fixed the Rust code so that it compiles. Please reconsider the downvotes and close votes. The accepted answer explains a concept that I did not get from Rust Book's chapter on borrowing, was very helpful to me, and could help others who are at the same point in learning Rust.
An item's mutability is essentially part of the name of the variable in rust. Take for example this code:
let mut foo = String::new();
let foo = foo;
let mut foo = foo;
foo suddenly becomes immutable, but it does not mean that the first two foos don't exist.
On the other hand, a mutable reference is attached to the lifetime of the object and is therefore type-bound, and will exist for its own lifetime, disallowing any kind of access to the original object if it is not through the reference.
let mut my_string = String::new();
my_string.push_str("This is ok! ");
let foo: &mut String = &mut my_string;
foo.push_str("This goes through the mutable reference, and is therefore ok! ");
my_string.push_str("This is not ok, and will not compile because `foo` still exists");
println!("We use foo here because of non lexical lifetimes: {:?}", foo);
The second call to my_string.push_str will not compile because foo can (in this case it is guaranteed to) be used afterwards.
Your specific question asks something similar to the following, but you don't even need multithreading to test this:
fn immutably_use_value(x: &str) {
println!("{:?}", x);
}
let mut foo = String::new();
let bar = &foo; //This now has immutable access to the mutable object.
let baz = &foo; //Two points are allowed to observe a value at the same time. (Ignoring `Sync`)
immutably_use_value(bar); //Ok, we can observe it immutably
foo.push_str("Hello world!"); //This would be ok... but we use the immutable references later!
immutably_use_value(baz);
This does not compile. If you could annotate the lifetimes, they'd look something similar to this:
let mut foo = String::new(); //Has lifetime 'foo
let bar: &'foo String = &foo; //Has lifetime 'bar: 'foo
let baz: &'foo String = &foo; //Has lifetime 'baz: 'foo
//On the other hand:
let mut foo = String::new(); //Has lifetime 'foo
let bar: &'foo mut String = &mut foo; //Has lifetime 'bar: mut 'foo
let baz: &'foo mut String = &mut foo; //Error, we cannot have overlapping mutable borrows for the same object!
A few extra notes:
Due to NLL (Non Lexical Lifetimes), the following code will compile:
let mut foo = String::new();
let bar = &foo;
foo.push_str("Abc");
Because bar is not used after the mutable use of foo.
You mention threading, which has its own constraints and traits involved:
The Send trait will allow you to give ownership of a variable across a thread.
The Sync trait will allow you to share a reference to a variable across a thread. This includes mutable references, as long as the original thread does not use the object for the duration of the borrow.
A few examples:
Type T is Send + Sync, it can be sent across threads and be shared between them
Type T is !Send + Sync, it can be shared across threads, but not sent between them. An example is a window handle that can only be destroyed on the original thread.
Type T is Send + !Sync, it can be sent across threads, but not shared between them. An example is RefCell, which will can only use its runtime borrow-checking on a single thread due to it not using atomics (Multithreading safe components).
Type T is !Send + !Sync, it can only live on the thread it was created on. An example is Rc, which cannot send a copy of itself across threads because it cannot count references atomically (Look at Arc to do that) and since it carries no lifetimes to force a single copy of itself to exist when sending across a thread boundary, it therefore cannot be sent across threads.
I use &str instead of &String in my third example, this is because String: Deref<str> (You may need to scroll down to see it), and therefore anywhere I need a &str I can chuck a &String in because the compiler will autoderef.
Related
still new to Rust and I am learning about the mutability rules of the language. I am trying to understand if structures, objects, arrays are deeply immutable.
So I have this struct
struct Foo {
bar: String,
}
And I would like to see what the rules for deep mutability are. When creating the struct, the struct must be defined as mut in order for it to have properties changed on it.
let mut f = Foo{
bar: String::from("hello")
};
f.bar = String::from("foo"); // Must be mut to change a property
println!("{:?}", f.bar);
However when I define an immutable struct and move it into a mutable vector, the struct becomes mutable.
let f = Foo{
bar: String::from("hello")
};
let mut strings: Vec<Foo> = vec![f];
strings[0].bar = String::from("foo");
println!("{:?}", strings[0].bar);
I am guessing that f is being copied into the mutable vector (and dropped from it's scope?) and the copy is being reassigned as mutable but I am not really sure.
Can I get some insight into what is going on?
mut is not a property of the value. It is a property of the binding (I am specifically not talking about mutable references, i.e. &mut, only let mut).
let v means "I will not attempt to change the value v contains through v, either directly or indirectly".
But once it's no longer within v, i.e. it was moved, it's fine to change it.
The following code, thus, compiles successfully:
let v = Foo { bar: String::new() };
let mut mutable_v = v;
mutable_v.bar = "abc".to_owned();
From the moment we moved the value out of v, the mutability of v is not relevant anymore.
And in the same manner, from the moment we moved the value out of f and into the vec strings, the mutability of f doesn't matter anymore, but the mutability of strings - because we don't change the value through f but through strings.
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>.
This question already has an answer here:
What are non-lexical lifetimes?
(1 answer)
Closed 3 years ago.
From Rust documentations:
mutable references have one big restriction: you can have only one mutable reference to a particular piece of data in a particular scope.
The example in the docs does support this statement:
let mut s = String::from("hello");
let r1 = &mut s;
let r2 = &mut s;
println!("{}, {}", r1, r2);
But this is a bit misleading to me. When print statement in the last line is removed the program compiles and runs fine (although with a couple of warnings). Later in the docs they say this is how rust deals with data races. I haven't reached the point where they explain threads and parallel programming but I guess it's still worthwhile to ask how are we preventing the data race in the following case:
fn main() {
let mut name: String = String::from("Devashish");
// assume that fun1 was called from a new thread
fun1(&mut name);
// fun1 and fun2 are being executed in parallel at this point
fun2(&mut name);
}
fn fun1(re: &mut String) {
println!("From fun1: {}", re);
}
fn fun2(re: &mut String) {
println!("From fun2: {}", re);
}
How are we preventing data race in the case above?
In this cases, the references are anonymous. The lifetime of the &mut name reference in your call to fun1 is the enclosing statement, that is fun1(&mut name);.
So the lifetime of the first mutable reference ends before the start of the second one.
If you had explicitly declared the references, the order would have determined whether there's a conflict (thanks to NLL).
This isn't valid:
let r1 = &mut name;
let r2 = &mut name;
fun1(r1);
fun2(r2);
This is valid (with a recent enough version of rustc):
let r1 = &mut name;
fun1(r1);
let r2 = &mut name;
fun2(r2);
because the borrow checker can determine that r1 isn't used anymore when r2 is declared.
And as you might discover if you try to put a new thread behind fun1: no, you can't pass the mutable borrow to another thread from inside fun1. As soon as fun1 ends, the mutable borrow is released, it's syntactically guaranteed.
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.
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