通过借用一个大小的 Range<T> 来迭代它

Iterate through a sized Range<T> by borrowing it

遍历 Range<T> 似乎消耗了范围实例,因为 into_iter 函数取得了范围的所有权。通过查看 documentation of range,可以清楚地看到 Borrow 特性仅针对动态范围对象实现。如果没有克隆,是否可以迭代一个范围,同时将范围的不可变引用传递给其他函数?

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() 是可用的,但这也会限制我们借用为不可变的,因为我们已经有同一个对象的可变借用。

首先,让我们看一下实现:

Range implements Iterator. for loops desugar to a call to std::iter::IntoIterator::into_iter, which is implemented for everything which is already an iterator (since you can obviously create an iterator from an iterator -- just return the iterator). Additionally, Iterator is implemented for (and only for) &mut references to existing iterators

由此我们可以推断出错误:

  • 您可以迭代任何实现了 IntoIterator.
  • 的东西
  • 您可以将任何 Iterator 变成 IteratorIntoIterator。什么都不做。
  • Iterator 仅针对对其他 Iterator 的唯一 (&mut) 引用实施。

因此,您不能迭代 &Ranges,而只能迭代 &mut Ranges 或 Ranges。

应该做的是 Clone the 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);

或者执行更惯用的操作,每次都构建范围:

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

这是非常便宜的。


此外,这些关于 &mut 而不是 & 引用的 Iterator 特征实现的规则适用于所有迭代器。这允许我们做这样的事情:

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 takes self,但是self&mut Range,所以我们没有用完原来的Range