Why would Rust allow multiple mutable references here? [duplicate] - rust

fn say_hello(s: &str) {
println!("Hello {}", s);
}
Why does this work
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
say_hello(x);
name.push_str(" Brown");
}
but this doesn't?
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
name.push_str(" Brown");
say_hello(x);
}
all I did was switch the order of the two functions but it seems like x has mutably borrowed name and push_str has also mutably borrowed name in both situations, so why does the first example compile?
If I take out the call to say_hello() why does the order of the two not matter even though there are still two mutable borrows?
Edit:
Is this similar?
fn change_string(s: &mut String) { // s is mutably borrowed but isn't used yet
println!("{}", s.is_empty()); // so the scopes don't overlap even though is_empty is making an immutable borrow?
s.push_str(" Brown");
}

One of Rust's borrowing rules is that mutable references are exclusive. Meaning, while x is alive, name cannot be used.
So, why does the first example compile even if x is still in scope? Because Rust also has non-lexical lifetimes meaning x stops "living" after its last use.
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
say_hello(x); // "x"s lifetime ends here, releasing the exclusive borrow
name.push_str(" Brown"); // "name" can be used again
}

Because in your first case, the two mutable borrow scopes don't overlap and you have only one borrow at any given point of time; but in your second case, they overlap, which means you have multiple mutable borrows at the certain point of time, which is not allowed:
First case:
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
say_hello(x); // the mutable borrow ends here
name.push_str(" Brown"); // a new mutable borrow
}
Second case:
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
name.push_str(" Brown"); // the first mutable borrow is still
// alive, you have two mutable borrows here
say_hello(x); // the first mutable borrow ends here
}

Related

Rust: Is it safe to cast a mutable borrow to a pointer and back (to placate the borrow checker), if I know that there will only be one instance of it?

I have a program that uses a QuadTree. This tree stores mutable borrows to data that is owned by another container (a Vec). I rebuild the QuadTree every game loop, but I do not want to reallocate, so I clear the underlying Vecs of the QuadTree instead of reconstructing it from scratch.
A simplified example that demonstrates the same problem is shown below. Instead of a QuadTree, here I am just using another Vec as this has identical issues.
struct A;
fn main() {
let mut owned_data = vec![A, A, A];
let mut mut_borrowed_data = vec![];
'_outer: loop {
mut_borrowed_data.clear();
'_inner: for borrow in &mut owned_data {
mut_borrowed_data.push(borrow);
}
}
}
This gives the error:
error[E0499]: cannot borrow `owned_data` as mutable more than once at a time
--> src\main.rs:8:30
|
8 | '_inner: for borrow in &mut owned_data {
| ^^^^^^^^^^^^^^^ `owned_data` was mutably borrowed here in the previous iteration of the loop
The issue isn't really that I am mutably borrowing in a previous iteration of the outer loop. If I remove the mut_borrowed_data.push(data); it compiles, because the borrow checker realises that the mutable borrow of owned_data is dropped at the end of each outer loop, therefore the number of mutable borrows is a max of 1. By pushing into mut_borrowed_data, this mutable borrow is moved into this container (Please correct me if I am wrong here), therefore it isn't dropped and the borrow checker is not happy. If I did not have the clear there would be multiple copies of the mutable borrow, and the borrow checker is not smart enough to realise that I only push into the mut_borrowed_data once, and that I clear it every outer loop.
But as it stands, there is only one instance of the mutable borrow at any one time, so is the following code safe/sound?
struct A;
fn main() {
let mut owned_data = vec![A, A, A];
let mut mut_borrowed_data = vec![];
'_outer: loop {
mut_borrowed_data.clear();
'_inner: for borrow in &mut owned_data {
let ptr = borrow as *mut A;
let new_borrow = unsafe { &mut *ptr };
mut_borrowed_data.push(new_borrow);
}
}
}
This now compiles. The mutable borrow of owned_data (named borrow) is not moved into the mut_borrowed_data and therefore it is dropped at the end of the outer loop. This means owned_data is only mutable borrowed once. The unsafe code takes a copy of the pointer to the data, dereferences it and creates a new borrow to that. (again, please correct me if I am wrong). Because this uses a copy and not a move, the compiler allows borrow and new_borrow to exist at the same time. This use of unsafe could break the borrow rules, but as long as I do not use borrow after I have created new_borrow, and as long as I clear mut_borrowed_data, then I think this is safe/sound.
Moreover, (I think) the guarantees given by the borrow checker still hold as long as I clear the mut_borrowed_data vec. It won't let me push into mut_borrowed_data twice in one loop, because the new_borrow is moved after it is first inserted.
I do not want to use a RefCell as I want this to be as performant as possible. The whole purpose of the QuadTree is to increase performance so I want to make any overhead it introduces as lean as possible. Incrementing the borrow count is probably cheap, but the branch (to check if that value is <= 1), the indirection, and the decreased simplicity of my data, are too much for me to feel happy about.
Is my use of unsafe here safe/sound? Is there anything that could trip me up?
Let's start with that: your code is safe, and also pretty sound.
The unsafe code takes a copy of the pointer to the data, dereferences it and creates a new borrow to that. (again, please correct me if I am wrong). Because this uses a copy and not a move, the compiler allows borrow and new_borrow to exist at the same time.
This is not accurate. The reason borrow and new_borrow can exist at the same time is not because raw pointers are copied while references are moved, but because when you converted the reference to a raw pointer you detached the lifetime chain - the compiler can no longer track the source of new_borrow.
It won't let me push into mut_borrowed_data twice in one loop, because the new_borrow is moved after it is first inserted.
Yes, but also no:
'_outer: loop {
mut_borrowed_data.clear();
'_inner: for borrow in &mut owned_data {
let ptr = borrow as *mut A;
let new_borrow = unsafe { &mut *ptr };
mut_borrowed_data.push(new_borrow);
mut_borrowed_data.push(new_borrow);
}
}
// Does not compile:
// error[E0382]: borrow of moved value: `new_borrow`
// --> src/lib.rs:12:32
// |
// 10 | let new_borrow = unsafe { &mut *ptr };
// | ---------- move occurs because `new_borrow` has type `&mut A`, which does not implement the `Copy` trait
// 11 | mut_borrowed_data.push(new_borrow);
// | ---------- value moved here
// 12 | mut_borrowed_data.push(new_borrow);
// | ^^^^^^^^^^ value borrowed here after move
// However, this does compile, and it is still Undefined Behavior:
'_outer: loop {
mut_borrowed_data.clear();
'_inner: for borrow in &mut owned_data {
let ptr = borrow as *mut A;
let new_borrow = unsafe { &mut *ptr };
mut_borrowed_data.push(new_borrow);
eprintln!("{borrow}"); // Use the old `borrow`.
}
}
You can make it a bit safer by shadowing the original borrow, so it can no longer be used:
'_outer: loop {
mut_borrowed_data.clear();
'_inner: for borrow in &mut owned_data {
let borrow = unsafe { &mut *(borrow as *mut A) };
mut_borrowed_data.push(borrow);
}
}
But it is still not perfect. The reason is that since you detach the lifetime, you get an unlimited, essentially 'static reference. This means it can be used longer than allowed, for example:
use std::sync::Mutex;
#[derive(Debug)]
struct A;
static EVIL: Mutex<Option<&'static mut A>> = Mutex::new(None);
fn main() {
let mut owned_data = vec![A, A, A];
let mut mut_borrowed_data = vec![];
'_outer: loop {
if let Some(evil) = EVIL.lock().unwrap().as_deref_mut() {
eprintln!("HaHa! We got two overlapping mutable references! {evil:?}");
}
mut_borrowed_data.clear();
'_inner: for borrow in &mut owned_data {
let borrow = unsafe { &mut *(borrow as *mut A) };
mut_borrowed_data.push(borrow);
}
*EVIL.lock().unwrap() = mut_borrowed_data.pop();
}
}
This does not mean this approach is bad (it is probably what I would use) but you need to be careful.

Rust cannot return value referencing local variable on HashMap get

I have a code looks like this:
use std::collections::HashMap;
fn main() {
let x = get_hash_map();
println!("{:?}", x);
}
fn get_hash_map() -> Option<&'static Vec<i32>> {
let mut hm = HashMap::new();
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
return hm.get("1");
}
But I got this error:
error[E0515]: cannot return value referencing local variable `hm`
--> src/main.rs:13:12
|
13 | return hm.get("1");
| --^^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `hm` is borrowed here
Here is the rust playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7605176aee2dd3ff77a0cfd04a89db55
Can anyone suggest the alternatives to fix this problem minimally? Thanks!
fn get_hash_map() -> Option<&'static Vec<i32>> {
let mut hm = HashMap::new();
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
return hm.get("1");
}
This is invalid, because you have declared you are going to return an Option<&'static Vec<i32>>, but you are returning an Option<&'a Vec<i32>> where 'a is the current function lifetime. The HashMap will stop existing as soon as the function returns, freeing the vector, and the reference will then become dangling. This is the exact kind of situation the borrow checker is designed to avoid.
Just return the vector by value:
fn get_hash_map() -> Option<Vec<i32>> {
let mut hm = HashMap::new();
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
return hm.remove("1");
}
remove moves the value out of the map, returning it as an Option<V>.
If you then need an Option<&Vec<i32>>, you can just use as_ref() on your Option<Vec<i32>> to get one. Always remember it will become invalid as soon as its value goes out of scope.
The HashMap hm is local to the scope of the get_hash_map() function and is dropped as soon as get_hash_map() returns. The value returned by hm.get("1") contains a reference to this HashMap, thus its lifetime is also tied to the scope of get_hash_map() which unfortunately is shorter than the ascribed 'static lifetime.
If you remove the 'static lifetime and replaced it by some 'a annotation on the function, you would get a similar error, since again there is no (sound) way to return borrowed data from a function that creates the owner of that data.
You can however create the map in the surrounding scope and pass it via a mutable reference to get_hash_map
use std::collections::HashMap;
fn main() {
let mut hm = HashMap::new();
let x = get_hash_map(&mut hm);
println!("{:?}", x);
}
// note that both `hm` and the reference in the return type have the same 'a lifetime.
fn get_hash_map<'a>(hm: &'a mut HashMap<String, Vec<i32>>) -> Option<&'a Vec<i32>> {
let mut vec = Vec::new();
vec.push(1);
hm.insert("1".to_string(), vec);
hm.get("1");
}

Why does the order of borrowing matter in rust?

fn say_hello(s: &str) {
println!("Hello {}", s);
}
Why does this work
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
say_hello(x);
name.push_str(" Brown");
}
but this doesn't?
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
name.push_str(" Brown");
say_hello(x);
}
all I did was switch the order of the two functions but it seems like x has mutably borrowed name and push_str has also mutably borrowed name in both situations, so why does the first example compile?
If I take out the call to say_hello() why does the order of the two not matter even though there are still two mutable borrows?
Edit:
Is this similar?
fn change_string(s: &mut String) { // s is mutably borrowed but isn't used yet
println!("{}", s.is_empty()); // so the scopes don't overlap even though is_empty is making an immutable borrow?
s.push_str(" Brown");
}
One of Rust's borrowing rules is that mutable references are exclusive. Meaning, while x is alive, name cannot be used.
So, why does the first example compile even if x is still in scope? Because Rust also has non-lexical lifetimes meaning x stops "living" after its last use.
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
say_hello(x); // "x"s lifetime ends here, releasing the exclusive borrow
name.push_str(" Brown"); // "name" can be used again
}
Because in your first case, the two mutable borrow scopes don't overlap and you have only one borrow at any given point of time; but in your second case, they overlap, which means you have multiple mutable borrows at the certain point of time, which is not allowed:
First case:
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
say_hello(x); // the mutable borrow ends here
name.push_str(" Brown"); // a new mutable borrow
}
Second case:
fn main() {
let mut name = String::from("Charlie");
let x = &mut name;
name.push_str(" Brown"); // the first mutable borrow is still
// alive, you have two mutable borrows here
say_hello(x); // the first mutable borrow ends here
}

Why does this not count as an immutable borrow?

I am reading the official Rust Book and looking at listing 4-8 in Section 4.3.
The code looks like this:
fn first_word(s: &String) -> usize {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return i;
}
}
s.len()
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear();
}
This line:
let word = first_word(&s);
seems to borrow an immutable reference to s. (This is where I guess I'm wrong; I just don't know why.)
In the next line, we mutate s by calling the clear() method.
I was expecting the compiler to throw:
cannot borrow `s` as mutable because it is also borrowed as immutable
Why does this compile?
The string s is immutably borrowed for the duration of the call to first_word. As soon as the control is returned to main after first_word, the string is not considered borrowed anymore and can be mutated as you have observed.
If first_word were to return a &String which you extended the lifetime of by assigning it to a variable, then you would see the error you expected. E.g.
fn first_word(s: &String) -> &String {
&s
}
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
s.clear();
}
cannot borrow s as mutable because it is also borrowed as immutable
https://rust.godbolt.org/z/cMVdVf
In that case, adding an extra scope would fix that:
fn main() {
let mut s = String::from("hello world");
{
let word = first_word(&s);
}
s.clear();
}

Why is `&mut &foo` valid but `&mut a_ref_to_foo` is invalid?

In the following example, t1 compiles but t2 does not.
Is there anything special about &mut &stream? I don't think Deref kicks in.
use std::net::TcpStream;
fn t1() {
let stream = TcpStream::connect("127.0.0.1").unwrap();
let a = &mut &stream;
}
fn t2(stream: &TcpStream) {
let a = &mut stream;
}
Playground
9 | fn t2(stream: &TcpStream) {
| ------ use `mut stream` here to make mutable
10 | let a = &mut stream;
| ^^^^^^ cannot borrow mutably
&mut &foo
This does two things - it creates a temporary value of type &Foo, then creates another temporary of type &mut &Foo.
let a_ref_to_foo = &foo;
&mut a_ref_to_foo
This also creates a temporary of type &mut &Foo, but to something that is observable via the variable binding a_ref_to_foo, not another temporary.
The problem is the same as this example:
// Works
String::new().clear();
// Doesn't work
let s = String::new();
s.clear();
When you have a temporary value, you have ownership over it which includes treating it as mutable. Once it's been assigned to a binding, the binding has ownership and you must indicate that it's allowed to be mutated or not.

Resources