我认为还有一些需要澄清的地方。集合类型(例如Vec<T>
和VecDeque<T>
)具有产生的into_iter
方法,T
因为它们实现IntoIterator<Item=T>
。Foo<T>
如果迭代了一个类型,没有什么可以阻止我们创建一个类型,它不会产生T
另一个类型U
。即Foo<T>
工具IntoIterator<Item=U>
。
实际上,有一些示例std
:&Path
Implements IntoIterator<Item=&OsStr>
and &UnixListener
Implements IntoIterator<Item=Result<UnixStream>>
。
into_iter
和之间的区别iter
回到原来的问题上的区别into_iter
和iter
。与其他人所指出的相似,区别在于into_iter
所要求的方法IntoIterator
可以产生中指定的任何类型IntoIterator::Item
。通常,如果一个类型实现IntoIterator<Item=I>
,按照约定它也有两个即席方法:iter
和iter_mut
分别产生&I
和&mut I
。
这意味着我们可以into_iter
通过使用特征绑定来创建一个接收具有方法(即,它是可迭代的)的类型的函数:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
但是,我们不能*使用特征绑定来要求类型具有iter
方法或iter_mut
方法,因为它们只是约定。我们可以说它into_iter
比iter
或更广泛地使用iter_mut
。
iter
和的替代方案iter_mut
观察到的另一个有趣之处是,这iter
并不是获得产生yield的迭代器的唯一方法&T
。按照惯例(再次),集合类型SomeCollection<T>
在std
其中有iter
方法也有其不可变的引用类型&SomeCollection<T>
实现IntoIterator<Item=&T>
。例如,&Vec<T>
Implements IntoIterator<Item=&T>
,因此它使我们能够迭代&Vec<T>
:
let v = vec![1, 2];
// Below is equivalent to: `for item in v.iter() {`
for item in &v {
println!("{}", item);
}
如果两者都v.iter()
等效&v
于该工具IntoIterator<Item=&T>
,那么Rust为什么要同时提供两者?用于人体工程学。在for
循环中,使用起来&v
比v.iter()
; 更加简洁。但在其他情况下,v.iter()
则比(&v).into_iter()
:
let v = vec![1, 2];
let a: Vec<i32> = v.iter().map(|x| x * x).collect();
// Although above and below are equivalent, above is a lot clearer than below.
let b: Vec<i32> = (&v).into_iter().map(|x| x * x).collect();
同样,在for
循环中,v.iter_mut()
可以替换为&mut v
:
let mut v = vec![1, 2];
// Below is equivalent to: `for item in v.iter_mut() {`
for item in &mut v {
*item *= 2;
}
何时为类型提供(实现)into_iter
和iter
方法
如果类型只有一个“方法”可以迭代,则我们应同时实现两者。但是,如果有两种或多种方法可以迭代,则应该为每种方法提供一种临时方法。
例如,String
既不提供,into_iter
也不提供,iter
因为有两种方法可以对其进行迭代:以字节为单位迭代其表示形式或以字符为单位迭代其表示形式。相反,它提供了两种方法:bytes
用于迭代字节和chars
用于迭代字符,作为iter
方法的替代方法。
*从技术上讲,我们可以通过创建特征来做到这一点。但是,然后我们需要impl
针对每个要使用的类型使用该特征。同时,已有多种类型的std
工具IntoIterator
。