0

I have a bunch of objects that implement some traits. For each object, I need to inject a vector of traits, which are listeners.

The problem I run into is that I need the objects to be both reference counted and mutable during initialization. Here's the construction:

impl Game {
    fn new() -> Self {
        let bg = Rc::new(Background::new());
        let time_counter = Rc::new(TimeCounter::new());
        let flag_counter = Rc::new(FlagCounter::new());
        let game_state_button = Rc::new(GameStateButton::new());
        let grid = Rc::new(Grid::new());

        let object_listeners: Vec<Weak<dyn ObjectListener>> = vec![
            Rc::downgrade(&game_state_button) as Weak<dyn ObjectListener>,
            Rc::downgrade(&flag_counter) as Weak<dyn ObjectListener>,
        ];
        grid.assign_object_listeners(object_listeners);

        let game_state_listeners: Vec<Weak<dyn GameStateListener>> = vec![
            Rc::downgrade(&time_counter) as Weak<dyn GameStateListener>,
            Rc::downgrade(&flag_counter) as Weak<dyn GameStateListener>,
            Rc::downgrade(&grid) as Weak<dyn GameStateListener>,
        ];
        game_state_button.assign_game_state_listeners(game_state_listeners);

        let flag_state_listeners: Vec<Weak<dyn FlagStateListener>> = vec![
            Rc::downgrade(&grid) as Weak<dyn FlagStateListener>,
        ];
        flag_counter.assign_flag_state_listeners(flag_state_listeners);

        let sprites: Vec<Rc<dyn Sprite>> = vec![
            Rc::clone(&bg) as Rc<dyn Sprite>,
            Rc::clone(&time_counter) as Rc<dyn Sprite>,
            Rc::clone(&flag_counter) as Rc<dyn Sprite>,
            Rc::clone(&game_state_button) as Rc<dyn Sprite>,
            Rc::clone(&grid) as Rc<dyn Sprite>,
        ];
        Game { sprites }
    }
}

The assignments will mutate the object, causing a problem for Rc. I could use RefCell, which works, but feels wrong. I've looked at channel, and I could create a bunch of channels and then inject them during object construction (either the send or receive), but that feels wrong as well. I've looked at specs and am still digesting it.

Can anyone suggest a better organization which encodes a graph of communication between objects? As a last resort, I could stuff the listeners into the Game object and inject a reference counted game object into each object, however, that feels twisted.

Here's how I solved lots of issues. I realized I had a communication graph, which the objects use. I constructed a helper to ease the wiring of channels to objects. I then construct the graph, and inject it into the objects during construction. This also cleaned up ownership, as Rc is no longer needed. Here's the code now:

pub fn new(layout: Layout) -> Game {
    // get all the channel wiring setup
    let mut channels = ChannelWiring::default();
    channels.wire::<Grid, Button>();
    channels.wire::<Grid, FlagCounter>();

    channels.wire::<Button, TimeCounter>();
    channels.wire::<Button, FlagCounter>();
    channels.wire::<Button, Grid>();

    channels.wire::<FlagCounter, Grid>();

    let mut sprites: Vec<Box<dyn Sprite>> = Vec::new();

    // create the underlying objects, and own via trait
    sprites.push(Box::new(Background {}));
    sprites.push(Box::new(TimeCounter::new(&mut channels)));
    sprites.push(Box::new(FlagCounter::new(layout, &mut channels)));
    sprites.push(Box::new(Button::new(layout, &mut channels)));
    sprites.push(Box::new(Grid::new(layout, &mut channels)));

    // finally create the game object
    Game { sprites: sprites }
}

With reference counting going away, so too did the need for RefCell.

Bruce
  • 503
  • 3
  • 13
  • *[RefCell] feels wrong* — why? *[a channel] feels wrong* — why? *that feels twisted* — perhaps that's an artifact of the problem space, not the language. Your `Rc` / `Weak` / `RefCell` is effectively a simple garbage collector. – Shepmaster Aug 03 '20 at 17:45
  • RefCell feels wrong, at least to me, as I never mutate the listeners once constructed. In the back of my mind, I'm feeling like I'm forcing things and RefCell is what I'm using to get around it. – Bruce Aug 05 '20 at 00:18

0 Answers0