iter和into_iter有什么区别?

vit*_*ral 117 rust

我正在做Rust示例教程,其中包含以下代码片段:

// 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));
Run Code Online (Sandbox Code Playgroud)

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

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

Mat*_* M. 98

第一个问题是:"什么是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;
}
Run Code Online (Sandbox Code Playgroud)

当您想要指定如何将特定类型转换为迭代器时,可以实现此特征.最值得注意的是,如果一个类型实现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>
Run Code Online (Sandbox Code Playgroud)

每个变体略有不同.

这个消耗它Vec和它的迭代器产生(T直接):

impl<T> IntoIterator for Vec<T> {
    type Item = T;
    type IntoIter = IntoIter<T>;

    fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

另外两个通过引用获取向量(不要被签名所欺骗,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(self) -> slice::Iter<'a, T> { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

虽然这个产生了可变引用:

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> { /* ... */ }
}
Run Code Online (Sandbox Code Playgroud)

所以:

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.

  • 我发现此博文有用:http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html (12认同)
  • @DanM.: (1) 这意味着`into_iter` 根据接收者是值、引用还是可变引用来选择一个实现。(2) Rust 中没有可变值,或者更确切地说,任何值都是可变的,因为您拥有所有权。 (2认同)

joe*_*joe 14

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

  • 谢谢...很难看出公认的答案如何阐明“iter”和“into_iter”之间的区别。 (14认同)
  • 是的,“`into_iter()` 迭代项目,将它们移动到新的范围”并不总是准确的 - 有关更多详细信息,请参阅已接受的答案。 (3认同)
  • 对于(常见?)“my_vec”或任何您调用的“.into_iter()”实际上是“Vec&lt;T&gt;”的情况,这是一个很好的简单答案。其他答案更复杂,还要提到的是,如果您对其他事物调用 .into_iter()` ,例如 `&amp;Vec&lt;T&gt;` 或 `&amp;[]` (或者等效地,尝试在 ` for ... in ... ` 循环),您实际上可能并未将项目移动到新范围中。(至少,这是我到目前为止的理解......) (2认同)

Dan*_*iel 9

我认为还有一点需要澄清。集合类型,例如Vec<T>and 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>>


之间的差异into_iteriter

回到原来的问题上的区别into_iteriter。与其他人指出的类似,不同之处在于它into_iter是一个必需的方法,IntoIterator它可以产生IntoIterator::Item. 通常,如果一种类型的工具IntoIterator<Item=I>,按照惯例它具有也有两个特别的方法:iteriter_mut其中产率&I&mut I分别。

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

fn process_iterable<I: IntoIterator>(iterable: I) {
    for item in iterable {
        // ...
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,我们不能*使用 trait 绑定来要求类型具有iter方法或iter_mut方法,因为它们只是约定。我们可以说它into_iteriteror更广泛使用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);
}
Run Code Online (Sandbox Code Playgroud)

如果在这两个工具中都v.iter()等价,那么为什么 Rust 提供两者?这是为了人体工程学。在循环中,它比;更简洁一些。但在其他情况下,比:&vIntoIterator<Item=&T>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();
Run Code Online (Sandbox Code Playgroud)

同样,在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;
}
Run Code Online (Sandbox Code Playgroud)

何时为类型提供(实现)into_iteriter方法

如果类型只有一种“方式”可以迭代,我们应该同时实现这两种方式。但是,如果有两种或更多的方式可以迭代,我们应该为每种方式提供一个特别的方法。

例如,String提供既不是into_iter也不iter是因为有两种方法可以迭代它:以字节为单位迭代其表示或以字符为单位迭代其表示。相反,它提供了两种方法:bytes用于迭代字节和chars用于迭代字符,作为iter方法的替代方法。


*好吧,从技术上讲,我们可以通过创建特征来做到这一点。但是我们需要为impl我们想要使用的每种类型使用该特征。同时,许多类型在std已经实现IntoIterator


vir*_*tor 6

.into_iter()没有为数组本身实现,但只是&[].相比:

impl<'a, T> IntoIterator for &'a [T]
    type Item = &'a T
Run Code Online (Sandbox Code Playgroud)

impl<T> IntoIterator for Vec<T>
    type Item = T
Run Code Online (Sandbox Code Playgroud)

由于IntoIterator仅定义了&[T],因此切片本身的删除方式与Vec使用值时不同.(值无法移出)

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