我正在做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是什么?


当前回答

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

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

对于my_vec中的x{…}本质上等价于my_vec.into_iter().for_each(|x|…)-两者都移动my_vec的元素到…范围。

如果您只需要查看数据,请使用iter,如果您需要编辑/修改数据,请使用iter_mut,如果您需要赋予它一个新的所有者,请使用into_iter。

这很有帮助:http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html

其他回答

IntoIterator和Iterator通常这样使用。

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

例如,让我们创建一个“新”数据结构:

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

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

相反,我们创建了一个中间的“iterable”结构,并实现了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 trait,它将由多个数据结构实现,例如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%正确的,生命周期和可变性支持被省略了。

我想有一些事情需要进一步澄清。集合类型,如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。

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

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

对于my_vec中的x{…}本质上等价于my_vec.into_iter().for_each(|x|…)-两者都移动my_vec的元素到…范围。

如果您只需要查看数据,请使用iter,如果您需要编辑/修改数据,请使用iter_mut,如果您需要赋予它一个新的所有者,请使用into_iter。

这很有帮助:http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html

TL; diana:

into_iter返回的迭代器可以产生T, &T或&mut中的任何一个,这取决于上下文。 按照惯例,iter返回的迭代器将产生&T。 根据约定,iter_mut返回的迭代器将生成&mut。


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

into_iter来自于IntoIterator特征:

pub trait IntoIterator 在哪里 <Self::IntoIter作为迭代器>::Item == Self::Item, { 类型项目; 类型IntoIter:迭代器; 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项目= T; type IntoIter = IntoIter<T>; fn into_iter(mut self) -> IntoIter<T>{/*…* /} }

另外两个通过引用获取向量(不要被into_iter(self)的签名所迷惑,因为在这两种情况下self都是一个引用),它们的迭代器将产生对Vec内部元素的引用。

它产生了不可变的引用:

impl<'a, T> IntoIterator for &'a Vec<T> { type Item = &'a T; type IntoIter = slice::Iter<'a, T>; fn into_iter(自我)- >片::Iter < T > { /* ...* /} }

而这个会产生可变引用:

impl<'a, T> IntoIterator for &'a mut Vec<T> { type Item = &'a mut T; type IntoIter = slice::IterMut<'a, T>; fn into_iter(自我)- >片::IterMut < T > { /* ...* /} }


So:

iter和into_iter有什么区别?

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

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

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

[T]没有实现IntoIterator;N],仅用于&[T;N] and &mut [T;N] -它将是Rust 2021。 如果没有为某个值实现方法,则会自动搜索该方法以查找对该值的引用

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

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

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

.into_iter()不会为数组本身实现,只会为&[]实现。比较:

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

with

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

由于IntoIterator仅在&[T]上定义,所以在使用这些值时,切片本身不能像Vec一样被删除。(值不能移出)

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