Я пытаюсь реализовать общие наблюдаемые коллекции для использования с инфраструктурой пользовательского интерфейса, которую я пишу. В настоящее время у меня возникла проблема с тем, что время жизни не может быть исключено в дженериках.
Для краткости следующие типы Callback
и Event
не представляют полную структуру событий, но проблему все равно следует выделить.
type Callback<Args> = Arc<dyn Fn(&Args)>;
struct Event<Args> {
handlers: Vec<Callback<Args>>,
}
struct OnChangedArgs<'a, T> {
items: std::slice::Iter<'a, T>,
}
struct ObservableList<T> {
items: Vec<T>,
on_changed: Event<OnChangedArgs<'_, T>>,
// ^^^^^ can't elid lifetime
}
Я могу решить свою проблему, написав свою систему событий неуниверсальным способом, но это может сильно раздражать дублирование кода.
struct OnChangedArgs<'a, T> {
items: std::slice::Iter<'a, T>,
}
pub type OnChangedCallback<T> = Arc<dyn Fn(&OnChangedArgs<T>)>;
// ^^^^^ lifetime elided
struct OnChangedEvent<T> {
handlers: Vec<OnChangedCallback<T>>,
}
struct ObservableList<T> {
items: Vec<T>,
on_changed: OnChangedEvent<T>,
}
Я тупой или это раздражает систему типов Rust???
Просто кажется, что система типов должна уметь видеть насквозь Event
, чтобы видеть, что она может скрывать время жизни. Таким образом, вам не придется излишне увеличивать время жизни, чтобы вам приходилось иметь дело с ним выше в другой структуре.
Вы не можете исключить время жизни в этом контексте, потому что нет ниоткуда, из чего можно было бы сделать вывод о жизни. Просто добавьте именованный параметр времени жизни к определяющему типу:
struct ObservableList<'a, T> {
items: Vec<T>,
on_changed: Event<OnChangedArgs<'a, T>>,
}
Это хорошо, потому что ясно показывает, что ObservableList
содержит заимствование.
Я не знаю, понравится ли вам это (или, в любом случае, это хорошая идея), но есть способ повысить рейтинг времени жизни (и, следовательно, исключить его), используя общие ассоциированные типы:
use std::sync::Arc;
type Callback<Args> = Arc<dyn Fn(&<Args as EventArgs>::Gatified<'_>)>;
trait EventArgs {
type Gatified<'a>;
}
struct Event<Args: EventArgs> {
handlers: Vec<Callback<Args>>,
}
impl<Args: EventArgs> Event<Args> {
fn trigger(&self, args: &Args::Gatified<'_>) {
for handler in &self.handlers {
handler(args);
}
}
}
struct OnChangedArgs<'a, T> {
items: std::slice::Iter<'a, T>,
}
impl<T: 'static> EventArgs for OnChangedArgs<'static, T> {
type Gatified<'a> = OnChangedArgs<'a, T>;
}
struct ObservableList<T: 'static> {
items: Vec<T>,
on_changed: Event<OnChangedArgs<'static, T>>,
}
impl<T: 'static> ObservableList<T> {
fn notify(&self) {
self.on_changed.trigger(&OnChangedArgs {
items: self.items.iter(),
});
}
}
OnChangedArgs<'static>
служит типом маркера (это может быть и другой тип), который позволяет GATифицировать любой OnChangedArgs
.