Я пытаюсь передать нестатическое замыкание в Токио. Очевидно, это не работает. Есть ли способ убедиться, что время жизни соответствует времени выполнения? Вот что я попробовал:
Попытка с помощью Arc
Чтобы не передавать замыкание непосредственно в Tokio, я поместил его в структуру, которая управляет нашими таймерами:
type Delays<'l, K: Eq + Hash + Debug + Copy + Send> = HashMap<K, Box<dyn FnOnce() + 'l + Send>>;
pub struct Timers<'l, K: Eq + Hash + Debug + Clone + Send> {
delays: Arc<Mutex<Delays<'l, K>>>,
}
impl
для этой структуры позволяет нам легко добавлять и удалять таймеры. Мой план состоял в том, чтобы каким-то образом передать статическое замыкание в tokio, просто переместив ссылку Weak
на мэш-таблицу с мьютексами:
// remember handler function
delays.insert(key.clone(), Box::new(func));
// create a weak reference to the delay map to pass into the closure
let weak_handlers = Arc::downgrade(&self.delays);
// task that runs after a delay
let task = Delay::new(Instant::now() + delay)
.map_err(|e| warn!("Tokio timer error: {}", e)) // Map the error type to ()
.and_then(move |_| {
// get the handler from the table, of which we have only a weak ref.
let handler = Weak::upgrade(&weak_handlers)
.ok_or(())? // If the Arc dropped, return an error and thus aborting the future
.lock()
.remove(&key)
.ok_or(())?; // If the handler isn't there anymore, we can abort aswell.
// call the handler
handler();
Ok(())
});
Так что с помощью Weak
мы гарантируем, что прервемся, если хештаблица была удалена.
Важно знать, что время жизни 'l
такое же, как и у структуры Timers
, но как я могу сказать компилятору? Кроме того, я думаю, что реальная проблема в том, что Weak<T>: 'static
не удовлетворен.
Пишу это сам, используя unsafe
Я пытался создать что-то похожее на Sc , чтобы достичьэто. Во-первых, Sc собирается работать здесь? Я читаю код и понимаю его. Я не вижу никаких очевидных проблем - хотя было довольно сложно прийти к выводу, что метод map
на самом деле безопасен, потому что ссылка обязательно будет отброшена в конце map
и не будет где-то сохранена.
Поэтому я попытался адаптировать Sc для своих нужд. Это всего лишь грубый набросок, и я знаю, что с этим есть некоторые проблемы, но я считаю, что что-то вроде этого должно быть возможным:
- Иметь
struct Doa<T>
, которому будет принадлежать T
Doa::ref(&self) -> DoaRef<T>
создаст непрозрачный объект, который внутри содержит *const u8
для принадлежащего объекта. DoaRef
не содержит ссылок с нестатическими временами жизни и, следовательно, можетбыть переданным tokio.
- Иметь
impl<T> Drop for Doa<T>
, который устанавливает *const u8
на null
- Так что
DoaRef
теперь может проверить, существует ли значение до сих пори получить ссылку на него.
Я также пытался убедиться, что время жизни &self
в ref
должно быть больше, чем время жизни ссылок в T
, чтобы убедиться, что это работаеттолько если Doa
действительно живет дольше, чем объект, на который указывает указатель.
struct Doa<'t, T: 'l> { ... }
pub fn ref(&'s self) -> DoaRef<T> where 't: 'a
Но тогда T
ограничен на протяжении жизни и, поскольку DoaRef
параметризован, DoaRef: 'static
не выполняетсябольше.
Или есть какой-то ящик, или, может быть, даже что-то в std
тшляпа может это сделать?