我认为还有一些需要澄清的地方。集合类型(例如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。