Почему Iterator::take_while вступает во владение итератором?

Я нахожу странным, что Iterator::take_while вступает во владение итератором. Это кажется полезной возможностью, чтобы иметь возможность взять первые элементы x, которые удовлетворяют некоторой функции, но все же оставляют остальные элементы доступными в исходном итераторе.

Я понимаю, что это несовместимо с ленивой реализацией take_while, но все еще чувствует себя полезным. Было ли это просто признано недостаточно полезным для включения в стандартную библиотеку, или есть какая-то другая проблема, которую я не вижу?

2 ответа

Решение

Все адаптеры итераторов берут исходный итератор по значению ради эффективности. Кроме того, владение оригинальным итератором позволяет избежать необходимости иметь время жизни, когда в этом нет необходимости.

Если вы хотите сохранить доступ к исходному итератору, вы можете использовать by_ref, Это вводит один уровень косвенности, но программист выбирает дополнительную работу, когда требуется эта функция:

fn main() {
    let v = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut i1 = v.iter();
    for z in i1.by_ref().take_while(|&&v| v < 4) {
    //         ^^^^^^^^^
        println!("Take While: {}", z);
    }

    for z in i1 {
        println!("Rest: {}", z);
    }
}

Имеет выход

Take While: 1
Take While: 2
Take While: 3
Rest: 5
Rest: 6
Rest: 7
Rest: 8

Вы заметили, что 4 скучал? Это потому, что однажды take_while выбирает значение и решает не использовать его, ему некуда "вернуть его". Чтобы вернуть его обратно, потребуется больше памяти и медлительность, чем всегда требуется.

Я использовал контейнер itertools для обработки подобных случаев, особенно take_while_ref,

Если это становится слишком сложным, возможно, мы использовали не тот инструмент.
Обратите внимание, что здесь присутствует 4:

      fn main() {
    let v = [1, 2, 3, 4, 5, 6, 7, 8];
    let mut i1 = v.iter().peekable();
    loop {
        match i1.peek() {
            Some(n) => {
                if n < &&4 {
                    println!("Take While: {}", n);
                } else {
                    println!("Rest: {}", n);
                }
            }
            None => return,
        }
        i1.next();
    }
}
      Take While: 1
Take While: 2
Take While: 3
Rest: 4
Rest: 5
Rest: 6
Rest: 7
Rest: 8

Да, ОП просил take_whileи решение Шепмастера превосходно.

Другие вопросы по тегам