0

I am trying to pass a reference to self down to a child struct that is a field variable

use std::cell::RefCell;
use std::rc::Rc;

pub struct BUS {
    pub processor: CPU,
    pub ram: [u8; 65536],
}

impl BUS {
    pub fn new() -> BUS {
        BUS {
            processor: CPU::new(),
            ram: [0; 65536],
        }
    }

    pub fn setup(&mut self) {
        self.processor.connect_bus(Rc::new(RefCell::new(self)));
        self.ram.iter_mut().for_each(|x| *x = 00);
    }

    pub fn write(&mut self, addr: u16, data: u8) {
        self.ram[addr as usize] = data;
    }

    pub fn read(&mut self, addr: u16, _read_only: bool) -> u8 {
        self.ram[addr as usize]
    }
}

pub struct CPU {
    bus_ptr: Rc<RefCell<BUS>>,
}

impl CPU {
    pub fn new() -> CPU {
        CPU {
            bus_ptr: Rc::new(RefCell::new(BUS::new())),
        }
    }
    pub fn connect_bus(&mut self, r: Rc<RefCell<BUS>>) {
        self.bus_ptr = r;
    }
    pub fn read_ram(&self, addr: u16, _read_only: bool) -> u8 {
        (self.bus_ptr.borrow_mut().read(addr, false))
    }
    pub fn write_ram(&mut self, addr: u16, data: u8) {
        (self.bus_ptr.borrow_mut().write(addr, data))
    }
}

fn main() {
    let comp = BUS::new();
    comp.setup();
}

Rust Playground

This errors:

error[E0308]: mismatched types
  --> src/main.rs:18:57
   |
18 |         self.processor.connect_bus(Rc::new(RefCell::new(self)));
   |                                                         ^^^^ expected struct `BUS`, found &mut BUS
   |
   = note: expected type `BUS`
              found type `&mut BUS`

I can't pass in self to the RefCell as it is a second mutable borrow. I got around this by moving my functions around but want to know how possible this structure is.

I achieved this in C++ by passing in this from BUS and then using *bus in connect_bus so that read_ram can be *bus->read(...).

Is it possible to call the BUS struct's read and write functions from a method on the CPU struct?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
JamesGill
  • 183
  • 11
  • 3
    It looks like your question might be answered by the answers of [Why can't I store a value and a reference to that value in the same struct?](https://stackoverflow.com/q/32300132/155423); [How do I express mutually recursive data structures in safe Rust?](https://stackoverflow.com/q/36167160/155423); . If not, please **[edit]** your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Dec 16 '19 at 20:57
  • *this is achievable in C++* — I'd expect that it's not possible in all cases. Specifically, try something like constructing a BUS & CPU in a function and then returning it. My guess is that calling methods at that point would cause memory unsafety (hopefully reported by Valgrind). – Shepmaster Dec 16 '19 at 21:03
  • 2
    See also [Pass self reference to contained object's function](https://stackoverflow.com/q/36936221/155423); [Passing mutable self reference to method of owned object](https://stackoverflow.com/q/30681468/155423) – Shepmaster Dec 16 '19 at 21:23

1 Answers1

4

The short answer is no.

  1. RefCell owns its inner object. This means it has the only copy of that object, so that it can fully control all access to it and not allow any other access from outside. An object can't exist in RefCell and outside of RefCell at the same time.

    Your setup could take an existing Rc<RefCell<BUS>> instead and pass that around. &mut BUS without the wrapper won't do.

  2. The borrow checker can't ensure safety of mutual parent-child relationships. It wants program data structured as trees or DAGs. Otherwise you're forced to use wrapper types or C-like raw pointers.

The borrow checker checks against interfaces, not implementation. If your setter borrows &mut self, that's exclusive borrow of the entire object, and for borrow checking that's the most restrictive and most inflexible option. You will need to peel some layers of abstraction to make this work, e.g. pass RAM down to the CPU. Alternatively, make RAM use Cell<u8> type, so that it can be mutated via shared references.

Kornel
  • 97,764
  • 37
  • 219
  • 309