Cannot assign to `something` because it is borrowed

Hi, I’m getting this error and hit a wall. Would anyone please let me know the right approach for what I’m doing here?

impl VirtualMachine {
    pub fn process(&mut self, block_: Rc<RefCell<Block>>) {
        let mut block_temp: Rc<RefCell<Block>>;
        let mut block = block_.borrow();

        self.pos = 0;
        while self.pos < block.instructions.len() {
            let instr = &block.instructions[self.pos];
            match instr {
                Instruction::Call(_options) => {
                    self.pos += 1;
                    // ERROR occurs here: cannot assign to `block_temp` because it is borrowed
                    block_temp = self.code_manager.get_block("123".to_string()).clone();
                    block = block_temp.borrow();
                    self.pos = 0;
                }
            }
        }
    }
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=6191f260ecb36a0a9f28cabccd306fa0

You are effectively creating a linked list. Have a read of Learn Rust With Entirely Too Many Linked ListsA Bad but Safe Doubly-Linked Deque - Iteration.

1 Like

Here’s one solution that compiles and passes all of the tests you provided. We use an outer loop that is distinct from the loop processing the current block. This loop is responsible for swapping to a new block once the current one is exhausted.

use std::{cell::RefCell, collections::BTreeMap, rc::Rc};

struct VirtualMachine {
    pos: usize,
    code_manager: CodeManager,
}

impl VirtualMachine {
    fn process(&mut self, mut block: Rc<RefCell<Block>>) {
        let mut next_block = None;

        'new_block: loop {
            self.pos = 0;

            if let Some(b) = next_block.take() {
                block = b;
            }

            let borrowed = block.borrow();

            while let Some(instr) = borrowed.instructions.get(self.pos) {
                match instr {
                    Instruction::Call => {
                        self.pos += 1;
                        next_block = Some(self.code_manager.get_block("123").clone());
                        continue 'new_block;
                    }
                }
            }
        }
    }
}

struct Block {
    instructions: Vec<Instruction>,
}

enum Instruction {
    Call,
}

struct CodeManager {
    blocks: BTreeMap<String, Rc<RefCell<Block>>>,
}

impl CodeManager {
    fn get_block(&self, id: &str) -> Rc<RefCell<Block>> {
        self.blocks[id].clone()
    }
}

(Playground)

That being said, I’m not the biggest fan of named loops, so I’d probably write it as two functions:

impl VirtualMachine {
    fn process(&mut self, block: Rc<RefCell<Block>>) {
        let mut block = Some(block);

        while let Some(current_block) = block {
            block = self.process_block(current_block);
        }
    }

    fn process_block(&mut self, block: Rc<RefCell<Block>>) -> Option<Rc<RefCell<Block>>> {
        self.pos = 0;
        let borrowed = block.borrow();

        while let Some(instr) = borrowed.instructions.get(self.pos) {
            match instr {
                Instruction::Call => {
                    self.pos += 1;
                    return Some(self.code_manager.get_block("123").clone());
                }
            }
        }

        None
    }
}
1 Like

I’ll give it a try. Thanks a lot.