Rust говорит, что параметр функции недостаточно активен, даже если задано надлежащее время жизни

#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;});
}
  

Копия Rust Playground

Комментарии:

1. Ну, я забыл сказать Box<T> , что требуется callback , чтобы быть 'static , потому что мне нужно преобразовать это общее в объекты признаков.

2. Неважно. На самом деле я не знал, что вы можете просто напрямую вставить generic, и он автоматически станет объектом trait?

3. @galaticExplorer он автоматически не становится объектом признака только потому, что вы его вставляете, но тип, реализующий признак , принудительно привязан к соответствующему объекту признака . Итак, поскольку вы запрашиваете объект признака (путем сохранения в a Vec<Box<dyn>> ), rustc знает, что нужно искать допустимое принуждение, находит его, и все в порядке.