Я пытаюсь написать простой игровой движок, но обычные шаблоны, которые я бы использовал для этого, здесь не работают из-за строгих правил заимствования.
Существует структура World
, которая владеет коллекцией Object
объектов-черт. Мы можем думать о них как о движущихся физических объектах в игровом движке. World
отвечает за вызов update
и draw
для каждого из них во время каждого игрового тика.
Структуре World
также принадлежит коллекция объектов Event
trait. Каждое из этих событий инкапсулирует произвольный фрагмент кода, который изменяет objects
во время игровых тиков.
В идеале, объект черты Event
имеет единственный метод do_event
, который не принимает аргументов, которые можно вызвать на World
во время каждого игрового тика. Проблема в том, что конкретный Event
должен иметь изменяемые ссылки на объекты, которые он изменяет, но эти объекты принадлежат World
. Мы могли бы World
передавать изменяемые ссылки на необходимые объекты, когда он вызывает do_event
для каждого события, но как узнать, какие объекты следует передавать каждому объекту события?
Если бы это было C ++ или Python, при создании конкретного Event
, я передаю цель события конструктору, где оно будет сохранено и затем использовано во время do_event
. Очевидно, что это не работает в Rust, потому что все цели принадлежат World
, поэтому мы не можем хранить изменяемые ссылки в другом месте.
Я мог бы добавить некоторую систему ID и таблицу поиска, чтобы World
знал, какие объекты пройти к которой Event
с, но это грязно и дорого. Я подозреваю, что весь мой подход к этому классу проблем должен измениться, но я в замешательстве.
Набросок моего кода:
trait Object {
fn update(&mut self);
fn draw(&self);
}
trait Event {
fn do_event(&mut self);
}
struct World {
objects: Vec<Box<dyn Object + 'a>>,
events: Vec<Box<dyn Event + 'a>>,
}
impl World {
fn update(&mut self) {
for obj in self.objects.iter_mut() {
obj.update();
}
for evt in self.events.iter_mut() {
evt.do_event();
}
}
fn draw(&mut self) { /*...*/ }
}
struct SomeEvent<'a, T: Object> {
target: &'a mut T,
}
impl<'a, T: Object> Event for SomeEvent<'a, T> {
fn update(&self) {
self.target.do_shrink(); // need mutable reference here
}
}
Я знаю, что RefCell
позволяет несколько объекты для получения изменяемых ссылок. Возможно, это то направление, в котором я должен go. Однако, основываясь на том, что я узнал из книги Rust , я понял, что RefCell
нарушает некоторые основные идеи Rust, вводя небезопасный код и циклические ссылки. Я думаю, мне просто интересно, есть ли какой-то другой очевидный шаблон проектирования, который больше соответствовал идиоматическому c способу делать вещи.