#generics #rust #closures #lifetime #trait-objects
#generics #Ржавчина #замыкания #время жизни #объекты-признаки
Вопрос:
Для фонового контекста: я создаю глобальную систему событий на основе наблюдателя / подписчика (используя единую общую систему событий). Я решил использовать FnMut
в качестве закрытия обратного вызова. Время жизни 'a
, указанное в impl
самодельной структуре Data<'a>
, должно позволять callback
параметру в методе mut_func()
жить столько же, сколько и вся Data
структура. Потому callback
что параметр использует F
общий, который определенно ограничен временем жизни 'a
. Но все еще появляется ошибка, указывающая, что callback
параметр недостаточно активен.
Первоначально я использовал Box<T>
в качестве своего контейнера для dyn FnMut(u32)
, но Box<T>
для этого требуется, чтобы обратный вызов был 'static
(потому что я помещаю общий в штучной упаковке в объекты с признаками), что в моем сценарии недостижимо (для удобства чтения). Затем я попытался использовать Rc<RefCell<T>>
, который, к сожалению, не поддерживает объекты признаков.
Кроме того, я использую generics для callback
параметра, потому что я хочу, чтобы эта функция имела более высокую читаемость, вместо того, чтобы видеть Box<T>
обертывание вокруг всего замыкания, которое будет повсюду, поскольку эта система событий будет моей центральной частью моей программы. Я сделаю все, чтобы сделать «интерфейс» более читаемым и чистым (за исключением значительного влияния на производительность).
Примечание: Это мой пример программы. Я могу опубликовать фактическую программу, если это необходимо.
Ошибка:
error[E0597]: `callback` does not live long enough
> | impl<'a> Data<'a> {
> | -- lifetime `'a` defined here
> | fn mut_func<F: FnMut(u32) -> () 'a>(amp;mut self, mut callback: F) {
> | self.o.push(amp;mut callback as amp;mut dyn FnMut(u32) -> ());
> | ------------^^^^^^^^^^^^^------------------------------
> | | |
> | | borrowed value does not live long enough
> | argument requires that `callback` is borrowed for `'a`
> | }
> | - `callback` dropped here while still borrowed
Пример:
use std::any::Any;
use std::mem;
use std::rc::Rc;
use std::cell::RefCell;
struct Event<'a> {
obs: Vec<amp;'a mut dyn FnMut(u32) -> ()>,
}
impl<'a> Event<'a> {
fn subscriber<F: FnMut(u32) -> () 'a>(amp;mut self, mut callback: F) {
self.o.push(amp;mut callback as amp;mut dyn FnMut(u32) -> ());
}
}
fn main () {
let mut e = Event {obs: Vec::new()};
let x = 3;
e.subscriber(|n| {x n;});
}
Ответ №1:
callback
параметр используетF
generic, который определенно ограничен временем жизни'a
. Но все еще появляется ошибка, указывающая, чтоcallback
параметр недостаточно активен.
callback
живет достаточно долго, но проблема в том, что вы не сохраняете callback
полученные данные, вы сохраняете их преобразованными в объект trait ( dyn
), и данные этого объекта trait должны принадлежать чему-то.
Первоначально я использовал
Box<T>
в качестве своего контейнера дляdyn FnMut(u32)
, ноBox<T>
для обратного вызова требуется'static
Нет, это не так. Это компилирует:
struct Event<'a> {
obs: Vec<Box<dyn FnMut(u32) -> () 'a>>,
}
impl<'a> Event<'a> {
fn subscriber<F: FnMut(u32) -> () 'a>(amp;mut self, callback: F) {
self.obs.push(Box::new(callback));
}
}
Затем с еще одним изменением ваш пример будет скомпилирован: define x
before e
, чтобы x
он жил дольше, чем e
:
fn main() {
let x = 3;
let mut e = Event {obs: Vec::new()};
e.subscriber(|n| {x n;});
}
Комментарии:
1. Ну, я забыл сказать
Box<T>
, что требуетсяcallback
, чтобы быть'static
, потому что мне нужно преобразовать это общее в объекты признаков.2. Неважно. На самом деле я не знал, что вы можете просто напрямую вставить generic, и он автоматически станет объектом trait?
3. @galaticExplorer он автоматически не становится объектом признака только потому, что вы его вставляете, но тип, реализующий признак , принудительно привязан к соответствующему объекту признака . Итак, поскольку вы запрашиваете объект признака (путем сохранения в a
Vec<Box<dyn>>
), rustc знает, что нужно искать допустимое принуждение, находит его, и все в порядке.