我正在进行Rust by Example教程,其中包含以下代码段:
// Vec example
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
// `iter()` for vecs yields `&i32`. Destructure to `i32`.
println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2));
// `into_iter()` for vecs yields `i32`. No destructuring required.
println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2));
// Array example
let array1 = [1, 2, 3];
let array2 = [4, 5, 6];
// `iter()` for arrays yields `&i32`.
println!("2 in array1: {}", array1.iter() .any(|&x| x == 2));
// `into_iter()` for arrays unusually yields `&i32`.
println!("2 in array2: {}", array2.into_iter().any(|&x| x == 2));
我很困惑 - 对于Vec
从iter
返回的迭代器产生引用,而从into_iter
返回的迭代器产生值,但对于数组,这些迭代器是相同的吗?
这两种方法的用例/ API是什么?
答案 0 :(得分:76)
第一个问题是:"什么是into_iter
?"
into_iter
来自IntoIterator
trait:
pub trait IntoIterator where <Self::IntoIter as Iterator>::Item == Self::Item, { type Item; type IntoIter: Iterator; fn into_iter(self) -> Self::IntoIter; }
如果要指定将特定类型转换为迭代器的方式,请实现此特征。最值得注意的是,如果类型实现IntoIterator
,则可以在for
循环中使用它。
例如,Vec
实现IntoIterator
...三次!
impl<T> IntoIterator for Vec<T> impl<'a, T> IntoIterator for &'a Vec<T> impl<'a, T> IntoIterator for &'a mut Vec<T>
每种变体略有不同。
这个消耗Vec
及其迭代器yields values(T
直接):
impl<T> IntoIterator for Vec<T> { type Item = T; type IntoIter = IntoIter<T>; fn into_iter(mut self) -> IntoIter<T> { /* ... */ } }
另外两个通过引用获取向量(不要被into_iter(self)
的签名所欺骗,因为self
在两种情况下都是引用)并且它们的迭代器将生成对元素的引用在Vec
内。
这一个yields immutable references:
impl<'a, T> IntoIterator for &'a Vec<T> { type Item = &'a T; type IntoIter = slice::Iter<'a, T>; fn into_iter(self) -> slice::Iter<'a, T> { /* ... */ } }
impl<'a, T> IntoIterator for &'a mut Vec<T> { type Item = &'a mut T; type IntoIter = slice::IterMut<'a, T>; fn into_iter(self) -> slice::IterMut<'a, T> { /* ... */ } }
所以:
iter
和into_iter
之间的区别是什么?
into_iter
是获取迭代器的通用方法,无论此迭代器是否产生值,不可变引用或可变引用是依赖于上下文的,有时可能会令人惊讶。
iter
和iter_mut
是临时方法。这适用于依赖于上下文的位,并且按照惯例,让您获得将产生引用的迭代器。
Rust by Example帖子的作者说明了依赖于调用into_iter
的上下文(即类型)的惊喜,并且还通过使用以下事实来复合问题: / p>
IntoIterator
未针对[T; N]
实施,仅适用于&[T; N]
和&mut [T; N]
这对into_iter
来说非常令人惊讶,因为所有类型([T; N]
除外)都为所有3种变体(值和引用)实现了它。数组不可能实现一个产生值的迭代器,因为它不能缩小&#34;收缩&#34;放弃它的物品。
至于为什么数组实现IntoIterator
(以这种令人惊讶的方式):它可以在for
循环中迭代对它们的引用。
答案 1 :(得分:21)
我(Rust新手)从Google来到这里,寻求一个简单的答案,而其他答案没有提供。这就是简单的答案:
iter()
通过引用遍历项目into_iter()
遍历项目,将其移至新范围iter_mut()
遍历所有项目,为每个项目提供可变的引用所以for x in my_vec { ... }
本质上等效于my_vec.into_iter().for_each(|x| ... )
-move
的两个my_vec
元素都属于...
范围。
如果您只需要“查看”数据,请使用iter
,如果您需要对其进行编辑/突变,请使用iter_mut
,如果需要给它一个新所有者,请使用into_iter
。
这很有帮助:http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html
将其设置为社区Wiki,以便我出错时希望Rust专业人士可以编辑此答案。
答案 2 :(得分:5)
&[]
未针对数组本身实现,而仅针对impl<'a, T> IntoIterator for &'a [T]
type Item = &'a T
实现。比较:
impl<T> IntoIterator for Vec<T>
type Item = T
与
IntoIterator
由于&[T]
仅在Vec
上定义,因此在使用这些值时,切片本身不能与"shiny"
相同。 (值无法移出)
现在,为什么这是一个不同的问题,我想学习自己。推测:数组本身就是数据,切片只是一个视图。在实践中,您不能将数组作为值移动到另一个函数中,只需传递它的视图,因此您也不能在那里使用它。
答案 3 :(得分:5)
我认为还有一些需要澄清的地方。集合类型(例如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>
和&UnixListener
implements IntoIterator<Item=Result<UnixStream>>
。 / p>
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
并不是获得产生&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
。