"cannot borrow `self.registers.d` as mutable more than once at a time" workaround

I know this has already been asked several times, but the solution given to other questions hasn't helped me.

    pub fn interpret_opcode(&mut self, opcode: u16) -> (u8, u8) {
        match opcode {
            0x14 => self.increment_8(&mut self.registers.d, 1),

            _ => {
                panic!("Unimplemented opcode {:x}!", opcode);
                (0, 0)
            }
        }
    }

Gives me this error:

error[E0499]: cannot borrow `*self` as mutable more than once at a time
  --> src/opcodes.rs:45:21
   |
45 |             0x14 => self.increment_8(&mut self.registers.d, 1),
   |                     ^^^^^-----------^---------------------^^^^
   |                     |    |           |
   |                     |    |           first mutable borrow occurs here
   |                     |    first borrow later used by call
   |                     second mutable borrow occurs here

error[E0499]: cannot borrow `self.registers.d` as mutable more than once at a time
  --> src/opcodes.rs:45:38
   |
45 |             0x14 => self.increment_8(&mut self.registers.d, 1),
   |                     -----------------^^^^^^^^^^^^^^^^^^^^^----
   |                     |    |           |
   |                     |    |           second mutable borrow occurs here
   |                     |    first borrow later used by call
   |                     first mutable borrow occurs here

Why does increment_8 need to borrow self? What other state does it mutate besides the target register? (This is partly a rhetorical question. By borrowing self, the function almost certainly borrows more than it strictly needs, which leads to your double-borrow problem.)

It mutates some of the struct values

    /// Update the flags, just the zero flag as of now.
    /// # Arguments
    /// * `result` - The result of an operation, if it's zero the zero flag will be set.
    fn update_flags(&mut self, result: u8) {
        if result == 0 {
            self.registers.f = 0x80;
        } else {
            self.registers.f = 0x00;
        }
    }

    /// Add to a register and handle flags.
    /// # Arguments
    /// * `register` - Mutable reference to a register.
    /// * `value` - The amount to add. It can also be negative.
    fn increment_8(&mut self, register: &mut u8, amount: i16) -> (u8, u8) {
        let test = 0;

        if amount >= 0 {
            *register = register.wrapping_add(amount as u8);
        } else {
            *register = register.wrapping_sub(amount as u8);
        }

        self.update_flags(*register);

        (1, 1) // Every 8 bit INC/DEC instruction has 1 byte and 1 cycle
    }

Then pass only a borrow of the fields it actually needs, instead of the entirety of self.

What do you mean? I never heard of something like this

That would be the "free variables" or (with more work) "view struct" options explored in this article. (Side note, the closure RFC mentioned is part of stable Rust now.)

Another option is to pass something that tells it what register to use instead of passing a &mut to the register.

1 Like

Might do the second one, thanks

I did the following, but nothing changed:

    /// Add to a register and handle flags.
    /// # Arguments
    /// * `register` - Mutable reference to a register.
    /// * `value` - The amount to add. It can also be negative.
    fn increment_8(&mut self, register: RegistersName, amount: i16) -> (u8, u8) {
        let register: &mut u8 = match register {
            A => &mut self.registers.a,
            B => &mut self.registers.b,
            C => &mut self.registers.c,
            D => &mut self.registers.d,
            E => &mut self.registers.e,
            F => &mut self.registers.f,
            H => &mut self.registers.h,
            L => &mut self.registers.l
        };

        if amount >= 0 {
            *register = register.wrapping_add(amount as u8);
        } else {
            *register = register.wrapping_sub(amount as u8);
        }

        self.update_flags(*register);

        (1, 1) // Every 8 bit INC/DEC instruction has 1 byte and 1 cycle
    }

The borrow created by calling self.update_flags conflicts with the register borrow. You need to change update_flags so it is not a method (does not take &mut self)

1 Like

in this paticular case, you can get away with:

let value = *register;
self.update_flags(value);

but it might not always be feasible if you have complicated data structure. the real problem is how to design your type: don't create a type that contains all the states just for convenient, especially when you use mutable states a lot. shared mutability is very cumbersome in rust, and that's actually a good thing IMO.

in some OO languages like Java or C#, you are forced to create a "top level class" that represent "the whole program", while it's real purpose is just to provide a static method as the entry point. people tend to replicate what they are familiar with, and get frustrated with rust.

Get the value out before the call to update flags, like in my Playground. (On mobile, unchecked.)

I tried that, but it doesn't change anything

Will try that once I can, looks promising!

Every function with &mut self will have this problem, because &mut self means exclusive access to ALL fields of self, so it always forbids having any argument that borrows from the same object.

This is an unfortunate limitation of Rust, and you just can't use &mut self methods if you want to be passing fields (or any data they hold) by reference.

This works, why the

        let result = *register;
        self.update_flags(result);

?
Without this I get an error, but shouldn't it be the same thing as just doing self.update_flags(*register)?

No, in the case where you get an error, register is still a borrow and it still exists when the subsequent function is being called. The compiler could in theory notice that it's only used for dereferencing and copying the pointed value, but in practice, that's a hard analysis (because it's still used and being read from).

In contrast, if you copy out of the borrow in a separate statement, then the compiler can see that the reference is not, in fact, being used in the subsequent function call, and so it can end the borrow early enough.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.