Как я могу сохранить векторные элементы с их исходным индексом?
Если у меня есть Vec
Я могу перебирать элементы, используя индекс через v.iter().enumerate()
, и я могу удалять элементы через v.retain()
. Есть ли способ сделать и то, и другое одновременно?
В этом случае индекс больше не может использоваться для доступа к элементу - это будет индекс элемента до запуска цикла.
Я могу реализовать это сам, но чтобы быть таким же эффективным, как .retain()
Мне нужно использовать unsafe
, чего я бы хотел избежать.
Вот результат, который я хочу:
let mut v: Vec<i32> = vec![1, 2, 3, 4, 5, 4, 7, 8];
v.iter()
.retain_with_index(|(index, item)| (index % 2 == 0) || item == 4);
assert(v == vec![1, 3, 4, 5, 4, 7]);
3 ответа
Timmmm и Hauleth довольно прагматичны. Я хотел предложить пару альтернатив.
Вот площадка с некоторыми тестами и тестами: https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=cffc3c39c4b33d981a1a034f3a092e7b
Это некрасиво, но если вы действительно хотите v.retain_with_index()
метод, вы можете немного скопировать retain
метод с новой чертой:
trait IndexedRetain<T> {
fn retain_with_index<F>(&mut self, f: F)
where
F: FnMut(usize, &T) -> bool;
}
impl<T> IndexedRetain<T> for Vec<T> {
fn retain_with_index<F>(&mut self, mut f: F)
where
F: FnMut(usize, &T) -> bool, // the signature of the callback changes
{
let len = self.len();
let mut del = 0;
{
let v = &mut **self;
for i in 0..len {
// only implementation change here
if !f(i, &v[i]) {
del += 1;
} else if del > 0 {
v.swap(i - del, i);
}
}
}
if del > 0 {
self.truncate(len - del);
}
}
}
так, чтобы пример выглядел так:
v.retain_with_index(|index, item| (index % 2 == 0) || item == 4);
Или... еще лучше, вы можете использовать функцию высшего порядка:
fn with_index<T, F>(mut f: F) -> impl FnMut(&T) -> bool
where
F: FnMut(usize, &T) -> bool,
{
let mut i = 0;
move |item| (f(i, item), i += 1).0
}
так что теперь пример будет выглядеть так:
v.retain(with_index(|index, item| (index % 2 == 0) || item == 4));
(я предпочитаю последнее)
Я нашел, по сути, тот же вопрос на форуме пользователей Rust. Они предложили это решение, которое неплохо:
let mut index = 0;
v.retain(|item| {
index += 1;
((index - 1) % 2 == 0) || item == 4
});
В то время это не было правильным решением, потому что порядок итерации retain()
не было гарантировано, но, к счастью для меня, кто-то в этом потоке задокументировал порядок, так что теперь это так.:-)
Если вы хотите перечислить, отфильтровать (сохранить), а затем собрать полученный вектор, я бы сказал, что нужно сделать именно это:
v.iter()
.enumerate()
.filter(|&(idx, &val)| val - idx > 0)
.collect()