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.