rust iter和into_iter之间有什么区别?

rsl1atfo  于 2022-11-24  发布在  其他
关注(0)|答案(5)|浏览(201)

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

bvpmtnay

bvpmtnay1#

TL;DR:

  • into_iter返回的迭代器可能产生T&T&mut T中的任何一个,具体取决于上下文。
  • 按照约定,iter返回的迭代器将生成&T
  • 按照约定,iter_mut返回的迭代器将生成&mut T

第一个问题是:“什么是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,它的迭代器产生(直接使用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内部元素的引用。
这个函数产生不可变的引用

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

因此:
iterinto_iter之间有什么区别?
into_iter是一个通用方法,用于获取迭代器,无论该迭代器生成值、不可变引用还是可变引用都取决于上下文,有时会令人惊讶。
iteriter_mut是ad-hoc方法。因此,它们的返回类型独立于上下文,并且通常分别是产生不可变引用和可变引用的迭代器。
Rust by Example帖子的作者说明了对调用into_iter的上下文(即类型)的依赖所带来的惊喜,并且还使用以下事实来使问题复杂化:

  1. IntoIterator没有为[T; N]实现,只为&[T; N]&mut [T; N]实现--它将用于Rust 2021。
    1.如果没有为某个值实现方法,则会自动搜索该值的 * 引用 *
    这对于X1 M30 N1 X是非常令人惊讶的,因为所有类型(除了X1 M31 N1 X)都对所有3种变体(值和引用)实现了它。
    数组实现了IntoIterator(以一种令人惊讶的方式),使得在for循环中迭代对它们的引用成为可能。
    从Rust 1.51开始,数组可以实现一个生成值的迭代器(通过array::IntoIter),但是IntoIterator的现有实现会自动引用makes it hard to implement by-value iteration via IntoIterator
p5cysglq

p5cysglq2#

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

  • 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

dba5bblo

dba5bblo3#

我认为还有一些事情需要进一步澄清。集合类型,比如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_iterIntoIterator的必需方法,它可以生成IntoIterator::Item中指定的任何类型。通常,如果一个类型实现了IntoIterator<Item=I>,按照约定,它也有两个特别的方法:iteriter_mut,其分别产生&I&mut I
这意味着我们可以创建一个函数,通过使用一个特征绑定来接收一个具有into_iter方法的类型(即它是一个可迭代的):

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

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

iteriter_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.iter()等价于&v,因为两者都实现了IntoIterator<Item=&T>,那么为什么Rust同时提供了这两个呢?这是出于人体工程学的考虑。在for循环中,使用&v比使用v.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
lnvxswe2

lnvxswe24#

.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那样删除切片本身。(不能移出值)
现在,为什么会这样是一个不同的问题,我想了解自己。推测:数组是数据本身,切片只是它的一个视图。实际上,你不能把数组作为一个值移动到另一个函数中,只是传递它的一个视图,所以你也不能在那里使用它。

gr8qqesn

gr8qqesn5#

IntoIteratorIterator通常是这样使用的。
我们实现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的好地方,对吗?是的,我们可以这样做,但是这会在某些方面限制我们。
相反,我们创建一个中间的“可迭代”结构并实现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>,这可以通过为List实现IntoIterator来完成。

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%正确,忽略了生存期和可变性支持。

相关问题