ссылкам на значения заимствованной итератором коллекции. Но сигнатура next() не позволяет использовать (&'a mut self)->'a. Для индексируемых по usize коллекций компилятор успокаивается на отправке &collection[i], но если условный collection имеет лишь свой .iter() - возникает заимствование от self, которое мне не нужно.
Задача - итерироваться по ссылкам на элементы коллекции, и при успешной обработке выбрасывать элемент (эдакий 'graceful' .drain()). Использую сейчас вариант с unsafe, который рекомендуют при удалении из set элемента, используя его же в качестве ключа, но хотелось бы без unsafe
struct IdHashDeleteAfterUseIterator<'a, Id> where Id: Eq + Hash {
set: &'a mut HashSet<Id>,
first_done: bool,
}
impl<'a, Id> IdHashDeleteAfterUseIterator<'a, Id> where Id: Eq + Hash {
fn new (set: &'a mut HashSet<Id>) -> IdHashDeleteAfterUseIterator<'a, Id> {
IdHashDeleteAfterUseIterator {set, first_done: false}
}
}
impl<'a, Id> Iterator for IdHashDeleteAfterUseIterator<'a, Id>
where Id: Eq + Hash {
type Item = &'a Id;
fn next(&mut self) -> Option<Self::Item> {
if self.first_done {
if let Some(value_as_key) = self.set.iter().next() {
// https://github.com/rust-lang/rust/issues/27804#issuecomment-406269067
let key_ref = unsafe{&*(value_as_key as *const _)};
self.set.remove(key_ref);
}
}
self.first_done = true;
//let opt = self.set.iter().next(); // error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
//opt
match self.set.iter().next() {
None => None,
Some(v) => {
// зато работает
let myref = unsafe{&*(v as *const _)};
Some(myref)
}
}
}
}
Спасибо
Стандартный трейт Iterator для такого не подходит. Возвращаемая им ссылка должна быть валидна на протяжении всего времени жизни итератора. Здесь ссылка инвалидируется, что дает возможность сделать UB в safe коде. Например: let a = iter.next(); let _ = iter.next(); a.something();
Так ведь в моём случае эта ссылка и будет валидна всю жизнь итератора, и даже дольше, ведь коллекция по ссылке живёт дольше самой ссылки по-определению. С чего бы возвращаемой ссылке инвалидироваться? Пример с a.something() не ведёт к ub, ведь 1. Итератор до сих пор жив 2. Для моего исходника, даже после drop итератора, set, заимствованный итератором, останется жив Можно подробнее?
self.set.remove(key_ref); удаляет элемент, все ссылки на него, в том числе a из моего примера становятся невалидными.
Дошло, спасибо У меня по логике есть гарантия неиспользования элемента за пределами текущей итерации Выходит, следует использовать while let Some(v) = my_next(&mut my_id_hash_delete_after_use_iterator) { … } Где fn my_next<‘a>(set: MyIdHashDeteAfterUseIterator) -> Option<&’a Id> { … } Или существует паттерн получше?
Дрейнить и вставлять неиспользованные элементы обратно. Использовать другую структуру данных, а не HashSet
Обсуждают сегодня