Inner mutability and dyn types

I am working on an embedded application (bare metal, rp2040, rtic). I do have alloc, but would like to limit how much of the code depends on it.

There is a bunch of my code that deals with the embedded nor flash. I started using the embedded-storage NorFlash trait, but ran into a bit of a problem as these traits use associated constants to convey information about the flash, and can't be used as trait objects. So, I'm using my own traits that make these queries regular methods. The things I'm trying to figure out how to do together are:

  1. The flash methods are mostly &mut self, even the read operation, because it might need to write to hardware to do that.
  2. Several parts of the code need to use the flash device. I could pass in the &mut F for the flash trait, but then I lose the association between one particular data structure and the flash device it represents. So, ideally, I'd like the flash device to be part of each structure that represents some aspect of the flash's state.
  3. What I'm doing now is using a BorrowMut<RefCell<F>> with appropriate constraints. This works for the case when I want the device to be stack allocated.
  4. I'd also like to be able to create a test environment (not embedded) that tests this code with multiple flash devices that are dynamically parameterized. But, I can't put a dyn inside of the RefCell, because it isn't sized. I could have my own type and just parameterize it (or a wrapper type that allocates a dyn NorFlash. Is it reasonable to just have this inside of the RefCell.

Just looking for ideas, and trying to make this code work both in an embedded situation, ideally with no alloc, as well as allow a test frame work to test it.

I think one other reason to maybe want the code to use dyn is code size. Part of the code that copies will work with two flash devices, and some of the other structs it instantiates would be for each device, which if just generic parameters would result in two copies of that code, as I understand.

Some additional information. This is what I came up with for the current type:

pub struct Image<F, R: Borrow<RefCell<F>>> {
    flash: R,
    phantom: PhantomData<F>,
    ...

then, parts of the code to implement things with this.

impl<F: ReadFlash, R: Borrow<RefCell<F>>> Image<F, R> {
    pub fn from_flash(flash: R) -> Result<Image<F, R>> {
        ...
        flash.borrow().borrow_mut().read(...)?;
        ...
    }
}

when I use this field, I end up with flash.borrow().borrow_mut().operation(...).

Perhaps I should make a wrapper that I can put the flash reference into that borrows and relays the messages down. But I can't come up with a way of doing that the doesn't end up still needing the type constraints everywhere it gets used.

Then the Upgrade struct needs two possibly different flash devices, and the types get really long.

I ended up using Rc<RefCell<dyn Flash>> in much of my code (with dyn ReadFlash where I can. This will probably upset some people, as I'm using both Rc and dyn in embedded.