Вы можете избежать повторного обхода списка, отслеживая последний элемент, который вы видели по ходу (и затем обновляя его в конце).
Если вы наивны в том, как вы это делаете, у вас будут проблемы;ваш «предыдущий» указатель сохраняет за собой право владения остальной частью списка, а средство проверки заимствования не позволяет этого.Хитрость заключается в том, чтобы разорвать эту ссылку по ходу дела - и для этого вы можете использовать функцию mem::replace
.Как только вы это сделаете, вы должны будете вернуть его обратно, прежде чем снова потерять след предыдущего узла.
Вот как это может выглядеть в полной мере (вам придется простить мое либеральное использование unwrap
- Я думаю, это проясняет ситуацию):
pub fn pop(&mut self) -> Option<T>
where T : Copy,
{
use std::mem::replace;
let curr = replace(&mut self.head, None);
if curr.is_none() { // list started off empty; nothing to pop
return None;
}
let mut curr = curr.unwrap(); // safe because of the check above
if let None = curr.next { // popped the last element
return Some(curr.data);
}
let mut prev_next = &mut self.head;
while curr.next.is_some() {
// Take ownership of the next element
let nnext = replace(&mut curr.next, None).unwrap();
// Update the previous element's "next" field
*prev_next = Some(curr);
// Progress to the next element
curr = nnext;
// Progress our pointer to the previous element's "next" field
prev_next = &mut prev_next.as_mut().unwrap().next;
}
return Some(curr.data);
}
В качестве отступления вся эта перестановка указателей сильно упрощается, если вы хотите немного изменить интерфейс, чтобы мы возвращали "новый"составлять список каждый раз (вступая во владение функцией pop
) или использовать постоянную структуру данных, как это делается в Learning Rust со слишком большим количеством связанных списков (уже упоминалось в комментарии):
pub fn pop_replace(self) -> (Option<T>, Self) {
// freely mutate self and all the nodes
}
Что бы вы использовали как:
let elem, list = list.pop();