我认为还有一些需要澄清的地方。集合类型,例如Vec<T> 和VecDeque<T>,具有产生T 的into_iter 方法,因为它们实现了IntoIterator<Item=T>。没有什么可以阻止我们创建一个类型Foo<T>,如果它被迭代,它将产生不是T,而是另一个类型U。即Foo<T>实现IntoIterator<Item=U>。
其实std中也有一些例子:&PathimplementsIntoIterator<Item=&OsStr>和&UnixListenerimplementsIntoIterator<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 方法的类型(即它是可迭代的),方法是使用 trait bound:
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
但是,我们不能*使用一个特征绑定来要求一个类型具有iter 方法或iter_mut 方法,因为它们只是约定。我们可以说into_iter 比iter 或iter_mut 的使用范围更广。
iter 和 iter_mut 的替代方案
要观察的另一件有趣的事情是iter 并不是获得产生&T 的迭代器的唯一方法。按照惯例(再次),std 中的集合类型 SomeCollection<T> 具有 iter 方法也有它们的不可变引用类型 &SomeCollection<T> 实现 IntoIterator<Item=&T>。比如&Vec<T>implementsIntoIterator<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 方法
如果该类型只有一种“方式”可以迭代,我们应该同时实现这两种方式。但是,如果有两种或多种方式可以迭代,我们应该为每种方式提供一个 ad-hoc 方法。
例如,String 既不提供 into_iter 也不提供 iter,因为有两种方法可以对其进行迭代:以字节为单位迭代其表示或以字符为单位对其表示进行迭代。相反,它提供了两种方法:bytes 用于迭代字节和chars 用于迭代字符,作为iter 方法的替代方法。
* 好吧,从技术上讲,我们可以通过创建特征来做到这一点。但是我们需要为我们想要使用的每种类型 impl 那个特征。同时std中的很多类型已经实现了IntoIterator。