I'm writing a virtual machine and currently working on its memory which is implemented using a linked list. I have implemented malloc which adds a new node to the list, free which marks a node to be reused by malloc and now I am working on realloc. The problem i have is that I cannot copy data between two nodes of the linked list due to there being two mutable references, here is a minimum example:
use std::collections::LinkedList;
struct MemoryBlock {
address: u64,
size: u64,
data: Vec<u8>
}
struct Memory {
blocks: LinkedList<MemoryBlock>,
}
impl Memory {
fn malloc(&mut self, alloc_size: u64) -> u64 {
self.blocks.push_back(MemoryBlock {
address: 1,
size: alloc_size,
data: vec![0x00]
});
1
}
fn realloc(&mut self, address: u64, new_size: u64) -> u64 {
let new_address = self.malloc(new_size);
let mut old_block = self.blocks.iter_mut().find(|b| b.address == address).unwrap();
let mut new_block = self.blocks.iter_mut().find(|b| b.address == new_address).unwrap();
new_block.data[0] = old_block.data[0];
new_address
}
}
fn main() {
let mut memory = Memory {
blocks: LinkedList::new()
};
memory.blocks.push_back(MemoryBlock {
address: 0,
size: 1,
data: vec![0x00]
});
memory.realloc(0, 2);
}
I have tried to make 'old_block' immutable but then I cannot have one mutable and one immutable borrow at the same time. Is there any way to structure my code differently or any other method (other than unsafe) to get it to work? I know I can use a vector and then use slices as a 'hack' to get it done but I would prefer to use a linked list if possible.
You can restructure the code so that the Rust compiler knows that old_block
and new_block
point to different locations. This will also be more efficient as the LinkedList
is only traversed once.
fn realloc(&mut self, address: u64, new_size: u64) -> u64 {
let new_address = self.malloc(new_size);
let mut old_block = None;
let mut new_block = None;
for block in &mut self.blocks {
if block.address == address {
old_block = Some(block);
} else if block.address == new_address {
new_block = Some(block);
}
}
new_block.unwrap().data[0] = old_block.unwrap().data[0];
new_address
}