iter和into_iter有什么区别?

我正在做生锈的例子教程,其中有以下代码片段:

// 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是什么?

99463 次浏览

.into_iter()并不用于数组本身,而仅用于&[]。比较:

impl<'a, T> IntoIterator for &'a [T]
type Item = &'a T

impl<T> IntoIterator for Vec<T>
type Item = T

因为IntoIterator只在&[T]上定义,所以当你使用这些值时,片本身不能像Vec那样被删除。(值不能移出)

为什么会这样是另一个问题了,我自己也想知道。推测:数组是数据本身,切片只是进入它的一个视图。实际上,你不能将数组作为值移动到另一个函数中,只能传递它的一个视图,所以你也不能在那里使用它。

TL;博士:

  • 根据上下文,into_iter返回的迭代器可以产生T&T&mut T中的任意一个。
  • 根据惯例,iter返回的迭代器将产生&T
  • 根据惯例,iter_mut返回的迭代器将产生&mut T

第一个问题是:“into_iter是什么?”

into_iter来自IntoIterator特征:

pub trait IntoIterator
where
<Self::IntoIter as Iterator>::Item == Self::Item,
{
type Item;
type IntoIter: Iterator;
fn into_iter(self) -> Self::IntoIter;
}

当您希望指定如何将特定类型转换为迭代器时,可以实现此trait。最值得注意的是,如果一个类型实现了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及其迭代器收益率< >强值< / >强(直接使用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内部元素的引用。

这个yield 不可变引用:

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> { /* ... */ }
}

而这个yield 可变引用:

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> { /* ... */ }
}

所以:

iterinto_iter之间的区别是什么?

into_iter是获取迭代器的泛型方法,无论该迭代器产生的是值、不可变引用还是可变引用,依赖于上下文有时会令人惊讶。

iteriter_mut是特别方法。因此,它们的返回类型与上下文无关,通常是分别产生不可变引用和可变引用的迭代器。

Rust by Example帖子的作者说明了来自于into_iter调用所依赖的上下文(即类型)的惊人之处,并且还通过使用以下事实来加剧问题:

  1. IntoIterator没有在[T; N]中实现,只在&[T; N]&mut [T; N]中实现——Rust 2021中将实现。
  2. 当一个方法没有为某个值实现时,它会自动搜索该值的参考文献

这对于into_iter来说是非常令人惊讶的,因为所有类型(除了[T; N])都为所有3个变量(值和引用)实现了它。

数组实现了IntoIterator(以一种令人惊讶的方式),从而可以在for循环中遍历对它们的引用。

从Rust 1.51开始,数组可以实现一个产生值的迭代器(通过array::IntoIter),但现有的IntoIterator实现会自动引用使得很难通过IntoIterator实现按值迭代

我从谷歌来到这里,寻求一个简单的答案,这是其他答案所没有提供的。答案很简单:

  • iter()通过引用迭代项
  • iter_mut()遍历项目,给出对每个项目的可变引用
  • into_iter()遍历这些项,将它们移动到新的作用域

因此,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

我想有一些事情需要进一步澄清。集合类型,如Vec<T>VecDeque<T>,具有into_iter方法,该方法产生T,因为它们实现了IntoIterator<Item=T>。没有什么可以阻止我们创建一个类型Foo<T>,如果它被迭代,它将不会产生T,而是另一个类型U。也就是说,Foo<T>实现了IntoIterator<Item=U>

事实上,在std中有一些例子:&Path 实现了 IntoIterator<Item=&OsStr>&UnixListener 实现了 IntoIterator<Item=Result<UnixStream>>


into_iteriter之间的区别

回到最初关于into_iteriter之间区别的问题。类似于其他人所指出的,区别在于into_iteriter0的必需方法,它可以产生iter1中指定的任何类型。通常,如果一个类型实现了IntoIterator<Item=I>,按照惯例,它还有两个特别方法:iteriter_mut,分别产生&I&mut I

这意味着我们可以创建一个函数,通过使用trait绑定来接收具有into_iter方法的类型(即它是一个可迭代对象):

fn process_iterable<I: IntoIterator>(iterable: I) {
for item in iterable {
// ...
}
}

然而,我们不能__abc5使用trait绑定来要求类型具有iter方法或iter_mut方法,因为它们只是约定。我们可以说,into_iteriteriter_mut更广泛地使用。

iteriter_mut的替代方案

另一件值得注意的有趣事情是,iter并不是获得产生&T的迭代器的唯一方法。根据约定(再次),std中具有iter方法的集合类型SomeCollection<T>也具有它们的不可变引用类型&SomeCollection<T>实现IntoIterator<Item=&T>。例如,&Vec<T> &T0 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循环中,使用&vv.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_iteriter方法

如果该类型只有一种可迭代的“方法”,那么我们应该同时实现这两种方法。然而,如果有两种或两种以上的方法可以迭代,我们应该为每种方法提供一个特别的方法。

例如,String既不提供into_iter也不提供iter,因为有两种方法来迭代它:迭代以字节表示的表示或迭代以字符表示的表示。相反,它提供了两个方法:bytes迭代字节,chars迭代字符,作为iter方法的替代。


嗯,技术上我们可以通过创建trait来实现。但是接下来我们需要为我们想要使用的每个类型impl该trait。同时,std中的许多类型已经实现了IntoIterator

IntoIteratorIterator通常这样使用。

对于具有内部/嵌套值(或位于引用后面)的结构,我们实现了IntoIterator,该结构要么实现了Iterator,要么具有中间的“迭代”;结构。

例如,让我们创建一个&;new&;数据结构:

struct List<T>;
// Looks something like this:
// - List<T>(Option<Box<ListCell<T>>>)
// - ListCell<T> { value: T, next: List<T> }

我们希望这个List<T>是可迭代的,所以这里应该是实现Iterator的好地方,对吗?是的,我们可以这样做,但这会在某些方面限制我们。

相反,我们创建了一个中间的“可迭代对象”;构造并实现Iterator特征:

// NOTE: I have removed all lifetimes to make it less messy.
struct ListIter<T> { cursor: &List<T> };
impl<T> Iterator for ListIter<T> {
type Item = &T;
fn next(&mut self) -> Option<Self::Item> {...}
}

所以现在我们需要以某种方式连接List<T>ListIter<T>。这可以通过实现IntoIterator for List来实现。

impl<T> IntoIterator for List<T> {
type Item = T;
type Iter = ListIter<Self::Item>;
fn into_iter(self) -> Self::Iter { ListIter { cursor: &self } }
}

IntoIterator也可以为容器结构实现多次,例如,如果它包含不同的嵌套可迭代字段,或者我们有一些更高类型的类型情况。

假设我们有一个Collection<T>: IntoIterator特征,它将由多个数据结构实现,例如List<T>Vector<T>Tree<T>,它们也有各自的Iter;ListIter<T>VectorIter<T>TreeIter<T>。但是当我们从泛型代码转换到特定代码时,这到底意味着什么呢?

fn wrapper<C>(value: C) where C: Collection<i32> {
let iter = value.into_iter() // But what iterator are we?
...
}

这段代码不是100%正确的,生命周期和可变性支持被省略了。