iter和into_iter有什么区别?

时间:2016-01-12 00:48:51

标签: rust

我正在进行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));

我很困惑 - 对于Veciter返回的迭代器产生引用,而从into_iter返回的迭代器产生值,但对于数组,这些迭代器是相同的吗?

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

4 个答案:

答案 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 valuesT直接):

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

这一个yields mutable references

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的上下文(即类型)的惊喜,并且还通过使用以下事实来复合问题: / p>

  1. IntoIterator未针对[T; N]实施,仅适用于&[T; N]&mut [T; N]
  2. 如果没有为某个值实现某个方法,则会自动搜索引用到该值,而不是
  3. 这对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_iteriter之间的区别

返回有关into_iteriter之间区别的原始问题。与其他人指出的相似,区别在于into_iterIntoIterator的必需方法,它可以产生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_iteriteriter_mut具有更广泛的用途。

iteriter_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循环中,使用&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