I'm fairly new to rust and have hit this conceptual wall with the compiler a couple of times. I'm sure this question and variants of it have been asked a hundred times, but I can't find the magic search terms. The short story is that I need two containers in a struct that keep references to the same set of data, but in different order. I can't figure out how to make the borrow checker happy.
I have this struct:
struct Node {
size: usize,
addr: usize
}
I need to keep these organized by both size
and by addr
. For the container, I started with what I thought would be simplest, a Vec
. I'm pretty sure I the content of the Vec
needs to be Rc<RefCell<Node>>
because I need to reference the same Node
from more than one Vec
-- is this understanding right?
use std::rc::Rc;
use std::cell::RefCell;
struct VecList {
by_addr: Vec<Rc<RefCell<Node>>>,
by_size: Vec<Rc<RefCell<Node>>>
}
impl VecList {
fn new(size: usize) -> Self {
let mut s = Self {
by_size: Vec::new(),
by_addr: Vec::new()
};
let new_node = Rc::new(RefCell::new(Node { size, addr: 0 } ));
s.by_size.push(Rc::clone(&new_node));
s.by_addr.push(Rc::clone(&new_node));
s
}
fn do_a_thing(&mut self, sz: usize) {
// find the node to do the thing to
let i = self.by_size.binary_search_by(
|node| node.borrow().size.cmp(&sz))
.unwrap_or_else(|e| e);
let rcnode = &mut self.by_size[i];
let mut node = rcnode.borrow_mut();
// do the thing
node.size -= sz;
node.addr += sz;
if node.size == 0 {
self.remove_node(rcnode);
}
}
fn remove_node(&mut self, _node: &Rc<RefCell<Node>>) {
// remove the node from both by_addr and by_size, letting the rc go to 0 and the node to be dropped
unimplemented!();
}
}
Errors:
error[E0499]: cannot borrow `*self` as mutable more than once at a time
--> src/lib.rs:41:13
|
33 | let rcnode = &mut self.by_size[i];
| ------------ first mutable borrow occurs here
...
41 | self.remove_node(rcnode);
| ^^^^ ------ first borrow later used here
| |
| second mutable borrow occurs here
I think I understand this error as: I have a &mut self
in the do_a_thing
method, then I try to take another &mut self
when I look up rcnode
. That basically makes sense.
What I can't figure out is how to avoid this as a design pattern. I've tried adjusting by_size
and by_addr
to be Box
ed, but with the same error. This doesn't seem like it should be hard, which makes me think I'm missing a core rust concept here. Can anybody point me in the right direction?