Reputation: 515
I have a piece of code which needs to operate on a list. This list contains items which come from another source and need to be processed and eventually removed. The list is also passed along to multiple functions which decide whether to add or remove an item. I created an example code which reflects my issue:
use std::{cell::RefCell, rc::Rc};
pub fn foo() {
let list: Rc<RefCell<Vec<Rc<RefCell<String>>>>> = Rc::new(RefCell::new(Vec::new()));
list.borrow_mut()
.push(Rc::new(RefCell::new(String::from("ABC"))));
while list.borrow().len() > 0 {
let list_ref = list.borrow();
let first_item = list_ref[0].borrow_mut();
//item processing, needed as mutable
list.borrow_mut().remove(0);
}
}
This panics at runtime:
thread 'main' panicked at 'already borrowed: BorrowMutError', src/libcore/result.rs:997:5
I think I understand the problem: I have two immutable borrows and then a third which is mutable. According to the Rust docs, this is not allowed: either many immutable borrows or a single mutable one. Is there any way to get around this issue?
Upvotes: 2
Views: 2116
Reputation: 12150
I have no idea what you are actually trying to achieve as you have failed to provide a minimal reproducible example, but I think you just mixed up the borrows of the list
and the item
in your data structure and that confused you in the first place.
Nonetheless the following code (which you can run in the playground) does what you have described above.
use std::{cell::RefCell, rc::Rc};
pub fn foo() {
let list = Rc::new(RefCell::new(Vec::new()));
let mut list = list.borrow_mut();
let item = Rc::new(RefCell::new(String::from("ABC")));
list.push(item);
println!("list: {:?}", list);
while let Some(item) = list.pop() {
println!("item: {:?}", item);
item.borrow_mut().push_str("DEF");
println!("item: {:?}", item);
}
println!("list: {:?}", list);
}
fn main() {
foo();
}
There are two tricks which I used here.
I borrowed the list
only once and that borrow was a mutable one, which allowed me to add and remove items from it.
Because your description said you want to remove the items from the list
anyway, I was able to iterate over the Vec
with the pop
or the remove
methods (depending on the order you wish to get the items from the list
). This means I didn't have to borrow the Vec
for the scope of the loop (which you would otherwise do if you would iterate over it).
There are other ways to remove an element based on some predicate. For example: Removing elements from a Vec based on some condition.
To actually answer your original question: there is no way to have an immutable and a mutable borrow at the same time safely. That is one of the core principles of Rust which makes it memory safe. Think about it, what kind of guarantee would immutability be if at the same time, under the hood, the data could actually change?
Upvotes: 3