Документация Iterator::peekable()
явно документирует это поведение:
Обратите внимание, что базовый итератор все еще продвигается, когда peek
вызывается впервые:чтобы извлечь следующий элемент, на базовом итераторе вызывается next
, поэтому любые побочные эффекты (то есть что-либо кроме извлечения следующего значения) метода next
будут иметь место.
, так как Peekable
работает с произвольными итераторами, он может использовать только стандартный интерфейс итератора для просмотра следующего элемента, и единственный способ получить следующий элемент из универсального Iterator
- это вызвать на нем next
.
Я бы хотел добавить немного больше контекста в то, что происходит в вашем коде.Структура Peekable
является адаптером итератора.Если у вас есть итератор iter
, вы можете вызвать iter.peekable()
, чтобы получить новый итератор, который поддерживает просмотр следующего элемента.Метод peekable()
принимает значение self
, что означает, что он использует исходный итератор.Таким образом, стандартный способ его использования - это код, подобный следующему:
let mut iter = "abc".chars().peekable();
Теперь iter
является итератором с возможностью просмотра, и peeking не продвигает iter
сам по себе, а только базовыйитератор, который обернут в Peekable
и больше не доступен напрямую.
Однако в вашем коде вы создаете новую оболочку Peekable
каждый раз, когда вызывается peek_first()
.Оболочка сбрасывается в конце peek_first()
.В вашей тестовой функции вы видите только базовый итератор, который продвигается каждый раз, как указано в документации.
Так почему же даже возможно сохранить доступ к базовому итератору, если peekable()
занимает self
по стоимости и потребляет это?Это из-за реализации переадресации черты Iterator
для изменяемых ссылок на итераторы :
impl<'_, I> Iterator for &'_ mut I
where
I: Iterator + ?Sized;
Метод peek_first()
получает self
по изменяемой ссылке, поэтому он можетне использовать базовый итератор.Вместо этого он использует реализацию перенаправления для изменяемых ссылок на итераторы и использует только изменяемую ссылку.
В качестве примечания, вы используете .and_then(|c| Some(*c))
, чтобы превратить Option<&T>
в Option<T>
.Для этого есть специальный метод, который называется cloned()
.