Итерировать по размеру Range <T>, заимствуя его - PullRequest
0 голосов
/ 20 июня 2020

Итерация через Range<T>, похоже, использует экземпляр диапазона, поскольку функция into_iter принимает владение диапазоном. Глядя на документацию для диапазона , становится ясно, что черта Borrow реализована только для динамических c объектов диапазона. Можно ли без клонирования выполнять итерацию по диапазону, одновременно передавая неизменные ссылки диапазона другим функциям?

let numbers = 500..4000;

// ERROR [(E0277)]: the trait `std::iter::Iterator` is not implemented for `&std::ops::Range<i32>`
for n in &numbers {
    println!("{}", n);

    do_something_else(&numbers);
    reuse_range(&numbers);
}

// Surprisingly, there are no errors when it comes to argument type of functions.
fn do_something_else(range: &Range<i32>) { }
fn reuse_range(range: &Range<i32>) { }

Как видно выше, функции могут заимствовать Range<T: Sized>, но сам компилятор не позволяет заимствовать диапазоны.

До сих пор я пробовал использовать Box интеллектуальный указатель, но поведение такое же. Просто by_ref() доступно, но это также ограничит нас от заимствования как неизменяемого, поскольку у нас уже будет изменяемое заимствование того же объекта.

1 Ответ

2 голосов
/ 20 июня 2020

Сначала давайте посмотрим на реализации:

Range реализует Iterator. for выполняет цикл десахара до вызова std::iter::IntoIterator::into_iter, что реализовано для всего, что уже является итератором (поскольку вы, очевидно, можете создать итератор из итератора - просто верните итератор). Кроме того, Iterator реализован для (и только) &mut ссылок на существующие итераторы .

Отсюда мы можем вывести ошибку:

  • Вы можете выполнять итерацию над всем, что реализует IntoIterator.
  • Вы можете превратить любое Iterator в * С 1026 * по IntoIterator. Что ничего не делает.
  • Iterator реализовано только для уникальных (&mut) ссылок на другие Iterator s.

Следовательно, вы не можете использовать итератор более &Range s, а вместо этого только &mut Range s или Range s.

Вместо этого нужно сделать либо Clone Range:

let my_range = 10..40;
for i in my_range.clone() {
    println!("{:?}", i);
}

Взять изменяемую ссылку на диапазон (тем самым опустошив и его) :

let mut my_range = 10..40;

for i in &mut my_range {
    println!("{:?}", i);
}

assert_eq!(my_range.next(), None);

Или проделайте более идиоматическое c действие, просто каждый раз увеличивая диапазон:

for i in 10..40 {
    println!("{:?}", i);
}

Это очень дешево.


Кроме того, эти правила о реализациях трейта Iterator для &mut, а не & ссылок, применяются ко всем итераторам. Это позволяет нам делать что-то вроде этого:

let mut my_iter = 0..100;

// Only take first 50 elements.
for x in (&mut my_iter).take(50) {
    println!("{:?} < 50", x);
}

for x in my_iter {
    println!("{:?} >= 50", x);
}

Обратите внимание, что take принимает self, однако self равно &mut Range, поэтому мы не используем вверх оригинал Range.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...