当前位置: 首页 > 知识库问答 >
问题:

iter和into\U iter有什么区别?

羊舌庆
2023-03-14

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

共有3个答案

桓嘉谊
2023-03-14

我认为有一些事情需要进一步澄清。集合类型,例如Vec

事实上,std中有一些示例

回到关于into_iteriter之间区别的原始问题。与其他人指出的类似,区别在于into_iterIntoIterator的必需方法,它可以产生IntoIterator::Item中指定的任何类型。通常,如果一个类型实现了IntoIterator

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

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

然而,我们不能使用一个trait绑定来要求类型具有iter方法或iter\u mut方法,因为它们只是约定。我们可以说,与iter或iter mut相比,iter的使用范围更广。

另一件有趣的事情是,iter并不是获得生成<代码>

let v = vec![1, 2];

// Below is equivalent to: `for item in v.iter() {`
for item in &v {
    println!("{}", item);
}

如果v.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();

类似地,在循环的中,可以用

let mut v = vec![1, 2];

// Below is equivalent to: `for item in v.iter_mut() {`
for item in &mut v {
    *item *= 2;
}

如果类型只有一种“方法”可以迭代,那么我们应该同时实现这两种方法。然而,如果有两种或两种以上的方法可以迭代,那么我们应该为每种方法提供一个特别的方法。

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

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

易嘉胜
2023-03-14
匿名用户

我(一个铁锈新手)从谷歌来到这里,寻求一个其他答案无法提供的简单答案。下面是一个简单的答案:

  • iter()通过引用迭代项目
  • 迭代项目,为每个项目提供可变引用
  • into\u iter()迭代项目,将其移动到新范围

因此,对于my\u vec{…}中的x本质上等同于my\u vec。into\u iter()。对于每个(| x |…)-两者都将my\u vec的元素移动到 范围。

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

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

把它变成一个社区wiki,这样如果我犯了任何错误,希望Rust专业人士可以编辑这个答案。

贺山
2023-03-14

TL;DR(灾难恢复):

  • into_iter返回的迭代器可能会产生任何T

第一个问题是:“iter中的内容是什么?”

into_iter来自IntoIteratortrait:

pub trait IntoIterator 
where
    <Self::IntoIter as Iterator>::Item == Self::Item, 
{
    type Item;
    type IntoIter: Iterator;
    fn into_iter(self) -> Self::IntoIter;
}

当您想要指定如何将特定类型转换为迭代器时,就可以实现这个特性。最值得注意的是,如果一个类型实现了迭代器,那么它可以在for循环中使用。

例如,Vec实现了迭代器。。。三次!

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

另外两个通过引用获取向量(不要被的签名愚弄到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> { /* ... */ }
}

所以:

iter和iter之间有什么区别?

into\u iter是一种获取迭代器的通用方法,无论该迭代器生成值、不可变引用还是可变引用都取决于上下文,有时可能会令人惊讶。

iter和iter-mut是特殊方法。因此,它们的返回类型独立于上下文,通常是迭代器,分别生成不可变引用和可变引用。

Rust by example帖子的作者说明了对调用into_iter的上下文(即类型)的依赖所带来的惊讶,并且还通过使用以下事实来加剧问题:

  1. Iterator(迭代器)不为[T;N]实现,仅为

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

数组在迭代器中实现了(以一种令人惊讶的方式),从而可以在循环中对它们的引用进行迭代。

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

 类似资料:
  • 问题内容: 因此,我正在观看RaymondHettinger的演讲“将代码转换为漂亮的惯用Python”,他提出了我从未意识到的这种形式。他的示例如下: 代替: 采用: 在查看的文档后,我发现了一个类似的示例: 这对我来说似乎很有用,但是我想知道您是否Pythonistas知道这种结构中不涉及I / O读取循环的任何示例吗?也许在标准库中? 我可以想到一些非常人为的示例,如下所示: 但这显然没有内

  • 问题内容: 我想调试Java程序的整个流程。Eclipse中(进入)和(进入)之间有什么区别? 问题答案: 考虑以下代码,将当前指令指针(将在下一步执行的行,由表示)放在in 的行,并由in 的行调用: 如果此时要 进入 ,则将移至中的行,进入函数调用。 如果要在那一步 结束 ,您将移至中的行,从而结束函数调用。 调试器的另一个有用功能是单步 退出 或单步 返回。 在这种情况下,单步返回基本上将使

  • 问题内容: 两者都意味着空间,但是有什么区别吗? 问题答案: 一个是不间断空间,另一个是常规空间。不间断的空格表示该行不应在该点处换行,就像它不会在一个单词的中间换行一样。 此外,正如斯文德(Svend)在其评论中指出的那样,不间断的空间不会崩溃。

  • 本文向大家介绍<%# %> 和 <% %> 有什么区别?相关面试题,主要包含被问及<%# %> 和 <% %> 有什么区别?时的应答技巧和注意事项,需要的朋友参考一下 答:<%# %>表示绑定的数据源 <%%>是服务器端代码块  

  • 问题内容: 以下代码之间有什么区别: 和 Python建议采用一种做事方式,但有时似乎不止一种。 问题答案: 一个是函数调用,一个是文字: 使用第二种形式。它更具Python风格,并且可能更快(因为它不涉及加载和调用单独的函数)。

  • 发展至今(2020 年 6 月份),GCC 编译器已经更新至 10.1.0 版本,其功能也由最初仅能编译 C 语言,扩增至可以编译多种编程语言,其中就包括 C++ 。 除此之外,当下的 GCC 编译器还支持编译 Go、Objective-C,Objective-C ++,Fortran,Ada,D 和 BRIG(HSAIL)等程序,甚至于 GCC 6 以及之前的版本还支持编译 Java 程序。但本