Learning backlink reference

For single-threaded mutable access, you can use Rc<RefCell>.

Rc does use ref counting, but the performance impact is almost non-existent because there is no atomic operation or synchronization. And the ref counting is only when you clone it (to add it to a new Lemming, for example) and when the clone is dropped (when the Lemming is dropped). The clone call does not copy the Game, it just increments the ref count.

RefCell is also doing some checking to enforce exclusive access to mutable data, but again the checking is extremely cheap.

Note that Rc will allocate the contained item (the Game) on the heap.

Here is a very short example:

(fixed to clone the Game before creating the Lemming):

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

        struct Game {
            field: usize,
        }

        struct Lemming {
            game: Rc<RefCell<Game>>,
        }

        impl Lemming {
            fn new(game: Rc<RefCell<Game>>) -> Self {
                Self { game }
            }

            fn f(&self) {
                let mut game = self.game.borrow_mut();
                game.field += 1;
            }
        }

        let game = Rc::new(RefCell::new(Game { field: 1 }));
        let lemming = Lemming::new(game.clone());
        lemming.f();

If you can't live with any runtime checks (of course, benchmarking is the only way to know the impact), or you don't want to allocate the Game or explicitly borrow it like this, then the solution is to pass the Game parameter whenever it is needed, rather than storing it in the Lemming. This is what I do, but not for performance reasons, it's just my personal preference -- I much prefer the static compiler checking of the borrowing rules.

Note that you can use Weak to avoid cycles, if you end up also using Rc for the Lemmings.

1 Like