我想有一些事情需要进一步澄清。集合类型,如Vec<T>和VecDeque<T>,具有生成T的into_iter方法,因为它们实现了IntoIterator<Item=T>。没有什么可以阻止我们创建一个类型Foo<T>,如果它被迭代,它将不会产生T,而是另一个类型U。也就是说,Foo<T>实现了IntoIterator<Item=U>。
事实上,std中有一些例子:&Path实现了IntoIterator<Item=&OsStr>,而&UnixListener实现了IntoIterator<Item=Result<UnixStream>>。
into_iter和iter的区别
回到最初关于into_iter和iter区别的问题。与其他人指出的类似,不同之处在于into_iter是IntoIterator的必需方法,它可以生成IntoIterator::Item中指定的任何类型。通常,如果一个类型实现了IntoIterator<Item=I>,按照惯例,它还有两个特别方法:iter和iter_mut,分别生成&I和&mut I。
这意味着我们可以创建一个函数,通过使用trait绑定来接收具有into_iter方法的类型(即它是一个可迭代对象):
fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}
然而,我们不能*使用trait绑定要求类型具有iter方法或iter_mut方法,因为它们只是约定。我们可以说into_iter比iter或iter_mut更广泛地使用。
iter和iter_mut的替代方案
另一件值得注意的事情是iter并不是获得产生&T的迭代器的唯一方法。按照约定(再次),std中包含iter方法的集合类型SomeCollection<T>也有它们的不可变引用类型&SomeCollection<T>实现IntoIterator<Item=&T>。例如,&Vec<T>实现了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.t iter()和&v都实现了IntoIterator<Item=&T>,那么为什么Rust同时提供这两个?这是为了人体工程学。在for循环中,使用&v比v.iter()更简洁;但在其他情况下,v.t 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方法的替代方案。
从技术上讲,我们可以通过创造一个特质来做到这一点。但接下来我们需要为我们想要使用的每一种类型强加该特征。同时,std中的许多类型已经实现了IntoIterator。