Why is this a mutable borrow? My thinking is calling get_index() is a mutable borrow, but once it returns a shared reference, that's just a shared borrow now. It should be fine to do other shared borrows, but the borrow checker doesn't see it that way. How do I fix this? Keep in mind this is a toy example, in the real code get_index does have good reason to take a &mut self.
struct Foo {
x: i64
}
impl Foo {
fn get_index(&mut self) -> Option<&i64> {
if self.x < 0 {
self.x = 0;
None
} else {
Some(&self.x)
}
}
fn use_index(&self, index: &i64) {
println!("{}", *index);
}
}
fn main ()
{
let mut f = Foo{x: 12};
if let Some(index) = f.get_index() {
f.use_index(index);
}
}
Compiling playground v0.0.1 (/playground)
error[E0502]: cannot borrow `f` as immutable because it is also borrowed as mutable
--> src/main.rs:24:9
|
23 | if let Some(index) = f.get_index() {
| ------------- mutable borrow occurs here
24 | f.use_index(index);
| ^^^^^^^^^^^^-----^
| | |
| | mutable borrow later used here
| immutable borrow occurs here
For more information about this error, try `rustc --explain E0502`.
error: could not compile `playground` due to previous error
https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=a11588bbb313b8e63e9dcc640c3aa471
Related
This question already has answers here:
Returning a reference from a HashMap or Vec causes a borrow to last beyond the scope it's in?
(1 answer)
When is it necessary to circumvent Rust's borrow checker?
(1 answer)
Closed 1 year ago.
use std::collections::HashMap;
#[derive(Clone, Hash, Eq, PartialEq)]
struct HeavyCloneKey;
struct Map<V> {
map : HashMap<HeavyCloneKey, V>
}
impl<V> Map<V> {
pub fn get_mut(&mut self, key: &HeavyCloneKey) -> Option<&mut V> {
if let Some(v) = self.map.get_mut(key) {
return Some(v);
}
// some logic to decide if create a new value;
if self.map.len() > 64 {
None
} else {
let v = create_v_with_long_code();
let v = self.map.entry(key.clone()).or_insert_with(|| v);
Some(v)
}
}
}
fn create_v_with_long_code<V>() -> V {
unimplemented!()
}
fn main() {
}
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=19c45ebfb1a39271ff5bd2443399596a
I can't understand why this can't work:
error[E0502]: cannot borrow `self.map` as immutable because it is also borrowed as mutable
--> src/main.rs:18:12
|
12 | pub fn get_mut(&mut self, key: &HeavyCloneKey) -> Option<&mut V> {
| - let's call the lifetime of this reference `'1`
13 | if let Some(v) = self.map.get_mut(key) {
| -------- mutable borrow occurs here
14 | return Some(v);
| ------- returning this value requires that `self.map` is borrowed for `'1`
...
18 | if self.map.len() > 64 {
| ^^^^^^^^ immutable borrow occurs here
These two borrows won't appear at the same time, what's the problem? How can I do this?
Although the code can be compiled by using contains_key before get_mut, it pays more overhead;
It helps a little to desugar the lifetimes by providing your own annotations on the references. It doesn't really matter here though as lifetimes aren't actually the issue.
pub fn get_mut<'s, 'k>(&'s mut self, key: &'k HeavyCloneKey) -> Option<&'s mut V> {
This gets us a little better error message:
error[E0502]: cannot borrow `self.map` as immutable because it is also borrowed as mutable
--> src/main.rs:18:12
|
12 | pub fn get_mut<'s, 'k>(&'s mut self, key: &'k HeavyCloneKey) -> Option<&'s mut V> {
| -- lifetime `'s` defined here
13 | if let Some(v) = self.map.get_mut(key) {
| -------- mutable borrow occurs here
14 | return Some(v);
| ------- returning this value requires that `self.map` is borrowed for `'s`
...
18 | if self.map.len() > 64 {
| ^^^^^^^^ immutable borrow occurs here
error[E0499]: cannot borrow `self.map` as mutable more than once at a time
--> src/main.rs:22:21
|
12 | pub fn get_mut<'s, 'k>(&'s mut self, key: &'k HeavyCloneKey) -> Option<&'s mut V> {
| -- lifetime `'s` defined here
13 | if let Some(v) = self.map.get_mut(key) {
| -------- first mutable borrow occurs here
14 | return Some(v);
| ------- returning this value requires that `self.map` is borrowed for `'s`
...
22 | let v = self.map.entry(key.clone()).or_insert_with(|| v);
| ^^^^^^^^ second mutable borrow occurs here
We have two errors here. Both stem from the rust compiler not being able to tell that we don't use the mutable borrow from line 13 later in the function.
The first says "you grabbed a mutable reference to self here so we can't let you have an immutable reference there". The second says "you grabbed a mutable reference to self here so we can't let you have a second mutable reference there".
How do we fix this? By making it clearer to the compiler that we'll never hold that first mutable reference. We can do this by manually checking if that key exists first. Change this
if let Some(v) = self.map.get_mut(key) {
return Some(v);
}
to this
if self.map.contains_key(key) {
return self.map.get_mut(key);
}
and it will compile.
Consider the following program. Each element should hold references to elements in the container that already existed at the time it was added.
struct Element<'a> {
previous: Vec<&'a Element<'a>>,
}
struct Container<'a> {
elements: Vec<Element<'a>>,
}
impl <'a>Container<'a> {
fn new() -> Container<'a> {
Container{elements: Vec::new()}
}
fn add(&'a mut self) -> &'a Element {
let previous = self.elements.iter().collect();
let element = Element{previous: previous};
self.elements.push(element);
&self.elements[self.elements.len() - 1]
}
}
fn main() {
let mut _c = Container::new();
}
This fails with the error
error[E0502]: cannot borrow `self.elements` as mutable because it is also borrowed as immutable
--> nested.rs:17:9
|
9 | impl <'a>Container<'a> {
| -- lifetime `'a` defined here
...
15 | let previous = self.elements.iter().collect();
| --------------------
| |
| immutable borrow occurs here
| argument requires that `self.elements` is borrowed for `'a`
16 | let element = Element{previous: previous};
17 | self.elements.push(element);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ mutable borrow occurs here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0502`.
But why is self.elements borrowed for 'a? I would expect it would no longer be borrowed after the collect() is done. I'd expect that each element that was in self.elements would be borrowed immutably because of their references now stored in element.previous. But self.elements should be free to be mutated now.
The error is : cannot borrow self.elements as mutable because it is also borrowed as immutable
// this is borrow self.selements
let previous:Vec<&Element> = self.elements.iter().collect();
previous is borrow from self.elements as immutable,
self.elements.push(element); use self.elements as mutable, you can't use mutable and immutable both
Here is my code, and the compiler error beneath.
fn main() {
let mut s = String::new();
let mut push_if = |b, some_str| {
if b {
s.push_str(some_str);
}
};
push_if(s.is_empty(), "Foo");
println!("{}", s);
}
error[E0502]: cannot borrow `s` as immutable because it is also borrowed as mutable
--> src/main.rs:8:13
|
3 | let mut push_if = |b, some_str| {
| ------------- mutable borrow occurs here
4 | if b {
5 | s.push_str(some_str);
| - first borrow occurs due to use of `s` in closure
...
8 | push_if(s.is_empty(), "Foo");
| ------- ^ immutable borrow occurs here
| |
| mutable borrow later used by call
Why is the compiler complaining about s.is_empty() being an immutable borrow?
I'm just trying to return a bool, which doesn't seem like I'm borrowing anything. What changes do I need to make to compile the program successfully?
You can try this:
fn main() {
let mut s = String::new();
let mut push_if = |b, some_str, string: &mut String| {
if b {
string.push_str(some_str);
}
};
push_if(s.is_empty(), "Foo", &mut s);
println!("{}", s);
}
I'm having trouble understanding why I can't use v a second time, when it seems that the first mutable borrow has gone out of scope:
fn get_or_insert(v: &mut Vec<Option<i32>>, index: usize, default: i32) -> &mut i32 {
if let Some(entry) = v.get_mut(index) { // <-- first borrow here
if let Some(value) = entry.as_mut() {
return value;
}
}
// error[E0502]: cannot borrow `*v` as immutable because it is also borrowed as mutable
while v.len() <= index { // <-- compiler error here
v.push(None);
}
// error[E0499]: cannot borrow `*v` as mutable more than once at a time
let entry = v.get_mut(index).unwrap(); // <-- compiler error here
*entry = Some(default);
entry.as_mut().unwrap()
}
playground link
Do I have my variable scopes wrong, or is the borrow checker protecting me from something I'm not seeing?
Edit: the error message with NLL enabled is pretty good:
error[E0502]: cannot borrow `*v` as immutable because it is also borrowed as mutable
--> src/main.rs:10:11
|
3 | fn get_or_insert(v: &mut Vec<Option<i32>>, index: usize, default: i32) -> &mut i32 {
| - let's call the lifetime of this reference `'1`
4 | if let Some(entry) = v.get_mut(index) {
| - mutable borrow occurs here
5 | if let Some(value) = entry.as_mut() {
6 | return value;
| ----- returning this value requires that `*v` is borrowed for `'1`
...
10 | while v.len() <= index {
| ^ immutable borrow occurs here
The key point is that, even with NLL, the lifetime of the return value spans the whole function. The fact that the function returns early on line 4 is not taken into account when deciding whether the v reference is accessible in the code lower down.
The fix suggested by #Stargateur is to grow the vector if needed before accessing an element:
fn get_or_insert(v: &mut Vec<Option<i32>>, index: usize, value: i32) -> &mut i32 {
if v.len() < index {
v.resize(index + 1, None);
}
v[index].get_or_insert(value)
}
playground link
Here's where I used the technique in the final code.
I'm trying to develop a message routing app. I've read the official Rust docs and some articles and thought that I got how pointers, owning, and borrowing stuff works but realized that I didn't.
use std::collections::HashMap;
use std::vec::Vec;
struct Component {
address: &'static str,
available_workers: i32,
lang: i32
}
struct Components {
data: HashMap<i32, Vec<Component>>
}
impl Components {
fn new() -> Components {
Components {data: HashMap::new() }
}
fn addOrUpdate(&mut self, component: Component) -> &Components {
if !self.data.contains_key(&component.lang) {
self.data.insert(component.lang, vec![component]);
} else {
let mut q = self.data.get(&component.lang); // this extra line is required because of the error: borrowed value does not live long enough
let mut queue = q.as_mut().unwrap();
queue.remove(0);
queue.push(component);
}
self
}
}
(Also available on the playground)
Produces the error:
error: cannot borrow immutable borrowed content `**queue` as mutable
--> src/main.rs:26:13
|
26 | queue.remove(0);
| ^^^^^ cannot borrow as mutable
error: cannot borrow immutable borrowed content `**queue` as mutable
--> src/main.rs:27:13
|
27 | queue.push(component);
| ^^^^^ cannot borrow as mutable
Could you please explain the error and it would be great if you can give me the right implementation.
Here is an MCVE of your problem:
use std::collections::HashMap;
struct Components {
data: HashMap<u8, Vec<u8>>,
}
impl Components {
fn add_or_update(&mut self, component: u8) {
let mut q = self.data.get(&component);
let mut queue = q.as_mut().unwrap();
queue.remove(0);
}
}
Before NLL
error[E0596]: cannot borrow immutable borrowed content `**queue` as mutable
--> src/lib.rs:11:9
|
11 | queue.remove(0);
| ^^^^^ cannot borrow as mutable
After NLL
error[E0596]: cannot borrow `**queue` as mutable, as it is behind a `&` reference
--> src/lib.rs:11:9
|
11 | queue.remove(0);
| ^^^^^ cannot borrow as mutable
Many times, when something seems surprising like this, it's useful to print out the types involved. Let's print out the type of queue:
let mut queue: () = q.as_mut().unwrap();
error[E0308]: mismatched types
--> src/lib.rs:10:29
|
10 | let mut queue: () = q.as_mut().unwrap();
| ^^^^^^^^^^^^^^^^^^^ expected (), found mutable reference
|
= note: expected type `()`
found type `&mut &std::vec::Vec<u8>`
We have a mutable reference to an immutable reference to a Vec<u8>. Because we have an immutable reference to the Vec, we cannot modify it! Changing self.data.get to self.data.get_mut changes the type to &mut &mut collections::vec::Vec<u8> and the code compiles.
If you want to implement the concept of "insert or update", you should check into the entry API, which is more efficient and concise.
Beyond that, Rust uses snake_case for method naming, not camelCase.