iter和into_iter有什么区别?


174

我正在做的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));

我很困惑-对于Veciteryields引用返回的迭代器和从into_iteryields值返回的迭代器,但是对于数组,这些迭代器是相同的吗?

这两种方法的用例/ API是什么?

Answers:


146

TL; DR:

  • 根据上下文,由返回的迭代器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;
}

当您要指定如何将特定类型转换为迭代器时,可以实现此特征。最值得注意的是,如果类型实现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是引用的签名所欺骗),并且它们的迭代器将生成对inside元素的引用Vec

产生了不可变的参考

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调用上下文(即类型)的依赖而带来的惊奇,并且还通过使用以下事实使问题更加复杂:

  1. IntoIterator未针对[T; N],仅针对&[T; N]和实现&mut [T; N]
  2. 如果没有为值实现方法,则会自动搜索该值的引用

这是非常令人惊讶的,into_iter因为所有类型(除外[T; N])都为所有3个变体(值和引用)实现了它。数组不可能实现产生值的迭代器,因为它不能“缩小”以放弃其项。

关于数组为什么实现IntoIterator(以一种令人惊讶的方式):使得有可能在循环中遍历对它们的引用for


14
我发现此博客文章有帮助:hermanradtke.com/2015/06/22/…–
poy

>此迭代器是否产生值,不可变的引用或可变的引用取决于上下文,这是什么意思,以及如何处理呢?例如,如何迫使iter_mut产生可变值?
Dan M.

@ DanM .:(1)表示into_iter根据接收者是值,引用还是可变引用来选择实现。(2)Rust中没有可变值,或者说,由于您拥有所有权,所以任何值都是可变的。
Matthieu M.

@ MatthieuM.hm,在我的测试中似乎并非如此。我实现了IntoIter为&'a MyStruct&mut 'a MyStruct,第一个总是选择如果存在的话,即使我叫into_iter().for_each()mut同值&mut的拉姆达参数。
Dan M.

1
@Ixx:谢谢,这非常有用。我决定在问题的顶部提供一个TL; DR,以避免将答案埋在中间,您对此有何看法?
Matthieu M.

77

我(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专业人士可以编辑此答案。


7
谢谢...很难看到已接受的答案是如何明确区分iter和的into_iter
mmw

那正是我想要的!
Cyrusmith

6

.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使用值那样被丢弃。(值不能移出)

现在,为什么是这样,这是一个不同的问题,我想学习一下自己。推测:数组是数据本身,切片只是对其的视图。实际上,您不能将数组作为值移动到另一个函数中,只能传递它的视图,因此也不能在那里使用它。


IntoIterator也为实现了&'a mut [T],因此可以将对象移出数组。我认为这与以下事实有关:return结构IntoIter<T>没有而有生命周期参数Iter<'a, T>,因此前者无法保存切片。
rodrigo

mut表示您可以更改值,而不是将其移出。
维普托

@rodrigo let mut a = ["abc".to_string()]; a.into_iter().map(|x| { *x });=>“错误:无法移出借用的内容”
viraptor

是的,我认为您是对的,不能从数组中移动值。但是,我仍然认为,ArrayIntoIter使用不安全的Rust作为库的一部分来实现某种结构应该是可能的……也许不值得,因为Vec无论如何都应该使用它们。
rodrigo

所以我不明白...是array.into_iter返回的原因&T-因为自动转换为它正在做魔术&array.into_iter-如果是这样,我不知道与移动值或不移动值有什么关系。还是就像@rodrigo所说的那样,仅仅因为(由于某种原因)您不能将值移出数组而得到引用?还是很困惑。
2013年

2

我认为还有一些需要澄清的地方。集合类型(例如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_iteriter。与其他人所指出的相似,区别在于into_iter所要求的方法IntoIterator可以产生中指定的任何类型IntoIterator::Item。通常,如果一个类型实现IntoIterator<Item=I>,按照约定它也有两个即席方法:iteriter_mut分别产生&I&mut I

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

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

但是,我们不能*使用特征绑定来要求类型具有iter方法或iter_mut方法,因为它们只是约定。我们可以说它into_iteriter或更广泛地使用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循环中,使用起来&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方法的替代方法。


*从技术上讲,我们可以通过创建特征来做到这一点。但是,然后我们需要impl针对每个要使用的类型使用该特征。同时,已有多种类型的std工具IntoIterator

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.