使用通用的迭代器,而不是具体名单类型 [英] Using generic iterators instead of specific list types

查看:165
本文介绍了使用通用的迭代器,而不是具体名单类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很新生锈,从C#来/ Java的/相似的。

I'm very new to Rust, coming from C# / Java / similar.

在C#中,我们有的IEnumerable< T> 可用于遍历几乎任何类型的数组或列表。 C#也有一个收益率,你可以用它来返回一个懒惰的名单关键字。下面是一个例子...

In C# we have IEnumerable<T> that can be used to iterate almost any kind of array or list. C# also has a yield keyword that you can use to return a lazy list. Here's an example...

// Lazily returns the even numbers out of an enumerable
IEnumerable<int> Evens(IEnumerable<int> input)
{
    foreach (var x in input)
    {
        if (x % 2 == 0)
        {
            yield return x;
        }
    }
}

这当然是一个愚蠢的例子。我知道我可以带锈的地图功能做到这一点,但我想知道如何创建我自己的方法接受并返回通用的迭代器。

This is a silly example of course. I know I could do this with Rust's map function, but I would like to know how to create my own methods that accept and return generic iterators.

据我所知,锈病有同样可以使用通用的迭代器,但他们都高于我的理解。我看 Iter项目 IntoIterator 的Iterator 类型,而且可能更多的文档,但没有很好地理解他们。

From what I can gather, Rust has generic iterators that can be use similarly, but they are above my understanding. I see Iter, IntoIterator, Iterator types, and probably more in documentation, but no good way to understand them.

任何人都可以提供如何创造一些像上面清楚的例子?谢谢!

Can anyone provide clear examples of how to create something like above? Thank you!

P.S。懒惰的方面是可选的。我更关心的是抽象的具体名单和数组类型了。

P.S. The lazy aspect is optional. I am more concerned with abstraction away from specific list and array types.

推荐答案

首先,忘掉 IntoIterator 及其他性状或类型。鲁斯特的核心迭代特点是的Iterator 。其下调的定义如下:

First, forget about IntoIterator and other traits or types. The core iteration trait in Rust is Iterator. Its trimmed down definition is as follows:

trait Iterator {
    type Item;  // type of elements returned by the iterator
    fn next(&mut self) -> Option<Self::Item>;
}

正如你可能知道,你可以把一个迭代的一些结构内部的光标。 的next()方法向前推进此光标,并返回在$ P $指出pviously的元素。当然,如果集合是疲惫,没有什么回报,所以的next()收益选项&LT;自::项目&GT; ,而不仅仅是自::项目

As you probably know, you can think of an iterator as a cursor inside of some structure. next() method advances this cursor forward, returning an element it pointed at previously. Naturally, if the collection is exhausted, there is nothing to return, and so next() returns Option<Self::Item>, not just Self::Item.

的Iterator 是一个特点,因此它可以通过特定类型的实现。需要注意的是的Iterator 本身的是不是一个正确的类型,你可以作为返回值或函数参数中使用 - 你必须使用的混凝土的实施而这种特质的类型。

Iterator is a trait, and so it can be implemented by specific types. Note that Iterator itself is not a proper type which you can use as a return value or a function argument - you have to use concrete types which implement this trait.

上面的语句听起来过于严格 - 如何使用任意的迭代器类型呢? - 但由于的泛型的情况并非如此。如果你想要一个函数接受任意的迭代器,只要它在相应的参数一般,添加的Iterator 绑定在相应类型的参数:

The above statement may sound too restrictive - how to use arbitrary iterator types then? - but because of generics this is not so. If you want a function to accept arbitrary iterators, just make it generic in the corresponding argument, adding an Iterator bound over the corresponding type parameter:

fn iterate_bytes<I>(iter: I) where I: Iterator<Item=u8> { ... }

返回的从函数迭代器可能很难,但请参阅下文。

Returning iterators from functions may be difficult, but see below.

例如,对&放大器的方法; [T] ,名为国际热核实验堆(),返回这将产生引用成片的迭代器。这个迭代器是此内容结构的实例。

For example, there is a method on &[T], called iter(), which returns an iterator which yields references into the slice. This iterator is an instance of this structure. You can see on that page how Iterator is implemented for Iter:

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;
    fn next(&mut self) -> Option<&'a T> { ... }
    ...
}

此结构用于保存原始片和里面的一些迭代状态的参考。它的的next()方法更新这个状态,并返回一个值,如果有任何。

This structure holds a reference to the original slice and some iteration state inside it. Its next() method updates this state and returns the next value, if there is any.

其类型实现任意值的Iterator 可以在循环(的使用循环其实可以与 IntoIterator ,见下文):

Any value whose type implements Iterator can be used in a for loop (for loop in fact works with IntoIterator, but see below):

let s: &[u8] = b"hello";
for b in s.iter() {
    println!("{}", b);   // prints numerical value of each byte
}

现在,的Iterator 特质其实比上面多​​了一个复杂。它还定义了大量的转换方法从而消耗他们是所谓的迭代器,并返回一个新的迭代莫名其妙地变换或从原来的迭代器过滤值。例如,枚举()方法返回从原来的迭代器与元素的位置号一起产生值的迭代器:

Now, Iterator trait is actually more complex than the above one. It also defines a lot of transformation methods which consume the iterator they are called on and return a new iterator which somehow transforms or filters values from the original iterator. For example, enumerate() method returns an iterator which yields values from the original iterator together with the positional number of the element:

let s: &[u8] = b"hello";
for (i, b) in s.iter().enumerate() {
    println!("{} at {}", b, i);   // prints "x at 0", "y at 1", etc.
}

枚举()的定义是这样的:

trait Iterator {
    type Item;
    ...
    fn enumerate(self) -> Enumerate<Self> {
        Enumerate {
            iter: self,
            count: 0
        }
    }
    ...
}

枚举只是其中包含一个迭代器和里面一个柜台,它实现了迭代℃的结构;项目=(USIZE,我::项目)&GT;

Enumerate is just a struct which contains an iterator and a counter inside it and which implements Iterator<Item=(usize, I::Item)>:

struct Enumerate<I> {
    iter: I,
    count: usize
}

impl<I> Iterator for Enumerate<I> where I: Iterator {
    type Item = (usize, I::Item);

    #[inline]
    fn next(&mut self) -> Option<(usize, I::Item)> {
        self.iter.next().map(|a| {
            let ret = (self.count, a);
            self.count += 1;
            ret
        })
    }
}

这个的就是大多数迭代器的转换来实现:每个转型是一个包装结构它通过委托包装了原来的迭代器并实施的Iterator 特点原来的迭代器并以某种方式改变所产生的价值。例如, s.iter()枚举()从上面的例子中返回类型的值枚举&LT; Iter项目&LT;'静,U8&GT;&GT ;

And this is how most iterator transformations are implemented: each transformation is a wrapping struct which wraps the original iterator and implements Iterator trait by delegating to the original iterator and transforming the resulting value somehow. For example, s.iter().enumerate() from the example above returns a value of type Enumerate<Iter<'static, u8>>.

请注意,虽然枚举()的Iterator 特征直接定义,它可以是一个独立的功能以及

Note that while enumerate() is defined in Iterator trait directly, it can be a standalone function as well:

fn enumerate<I>(iter: I) -> Enumerate<I> where I: Iterator {
    Enumerate {
        iter: iter,
        count: 0
    }
}

该方法的工作原理非常相似 - 它只是使用隐类型参数,而不是一个明确一个名为

The method works very similarly - it just uses implicit Self type parameter instead of an explicitly named one.

您可能不知道 IntoIterator 特征是什么。那么,它仅仅是一个方便的转换特性可以由任何类型的可转换为一个迭代进行:

You may wonder what IntoIterator trait is. Well, it is just a convenience conversion trait which can be implemented by any type which can be converted to an iterator:

pub trait IntoIterator where Self::IntoIter::Item == Self::Item {
    type Item;
    type IntoIter: Iterator;

    fn into_iter(self) -> Self::IntoIter;
}

例如,&放大器;'一[T] 可转换成 Iter项目&LT;'A,T&GT; ,因此它具有以下实现:

For example, &'a [T] can be converted into Iter<'a, T>, and so it has the following implementation:

impl<'a, T> IntoIterator for &'a [T] {
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;

    fn into_iter(self) -> Iter<'a, T> {
        self.iter()  // just delegate to the existing method
    }
}

这特点就是对于大多数容器类型和这些类型的引用来实现。事实上,这是对使用循环 - 它实现了 IntoIterator 在<$ C可以使用任何类型的值$ C>在子句:

This trait is implemented for most container types and references to these types. It is in fact used by for loops - a value of any type which implements IntoIterator can be used in in clause:

let s: &[u8] = b"hello";
for b in s { ... }

这是从学习和阅读的角度来看,因为它有更少的噪音非常不错(在的形式ITER()式的方法)。它甚至允许这样的事情:

This is very nice from learning and reading perspective because it has less noise (in form of iter()-like methods). It even allows things like these:

let v: Vec<u8> = ...;

for i in &v { /* i is &u8 here, v is borrowed immutably */ }
for i in &mut v { /* i is &mut u8 here, v is borrowed mutably */ }
for i in v { /* i is just u8 here, v is consumed */ }

这是可能的,因为 IntoIterator &放大器实现方式不同; VEC&LT; T&GT; &安培; MUT VEC&LT; T&GT; 和刚 VEC&LT; T&GT;

This is possible because IntoIterator is implemented differently for &Vec<T>, &mut Vec<T> and just Vec<T>.

每个的Iterator 工具 IntoIterator 它执行的标识转换( into_iter()仅返回它呼吁迭代器),所以你可以使用的Iterator 实例循环为好。

Every Iterator implements IntoIterator which performs an identity conversion (into_iter() just returns the iterator it is called on), so you can use Iterator instances in for loops as well.

因此​​,有意义的使用 IntoIterator 中的通用功能,因为它会使API为用户更方便。例如,枚举()函数从上面可以改写为这样的:

Consequently, it makes sense to use IntoIterator in generic functions because it will make the API more convenient for the user. For example, enumerate() function from above could be rewritten as such:

fn enumerate<I>(source: I) -> Enumerate<I::IntoIter> where I: IntoIter {
    Enumerate {
        iter: source.into_iter(),
        count: 0
    }
}


现在你可以看到泛型如何使用轻松实现与静态类型转换。锈没有如C#/ Python的任何收益(但它是最想要的功能之一,所以有一天它可能会出现在语言!),因此需要包源迭代器明确。例如,你可以写一些类似于上述枚举结构,你想要做的任务。


Now you can see how generics can be used to implement transformations with static typing easily. Rust does not have anything like C#/Python yield (but it is one of the most desired features, so one day it may appear in the language!), thus you need to wrap source iterators explicitly. For example, you can write something analogous to the above Enumerate structure which does the task you want.

然而,最惯用的方法是利用现有的组合子做的工作适合你。例如,您的code可以写成如下:

However, the most idiomatic way would be to use existing combinators to do the work for you. For example, your code may be written as follows:

let iter = ...;  // iter implements Iterator<Item=i32>
let r = iter.filter(|&x| x % 2 == 0);  // r implements Iterator<Item=i32>
for i in r {
    println!("{}", i);  // prints only even items from the iterator
}

不过,使用组合子可当你要编写自定义组合子功能,因为很多现有的组合器功能接受闭包变得丑陋(如过滤器()上面一个),但在封锈实现为匿名类型的值,所以只是没有办法写函数的签名返回迭代出来:

However, using combinators may turn ugly when you want to write custom combinator functions because a lot of existing combinator functions accept closures (e.g. the filter() one above), but closures in Rust are implemented as values of anonymous types, so there is just no way to write the signature of the function returning the iterator out:

fn filter_even<I>(source: I) -> ??? where I: IntoIter<Item=i32> {
    source.into_iter().filter(|&x| x % 2 == 0)
}

有解决此几种方法,其中之一是使用的特质对象的:

There are several ways around this, one of them is using trait objects:

fn filter_even<'a, I>(source: I) -> Box<Iterator<Item=i32>+'a>
    where I: IntoIterator<Item=i32>, I::IntoIter: 'a
{
    Box::new(source.into_iter().filter(|&x| x % 2 == 0))
}

下面我们躲在一个特质对象落后返回的实际迭代式过滤器()。请注意,为了使功能完全通用的,我不得不一辈子参数和相应的绑定添加到特质对象和我:: IntoIter 相关联的类型。这是必要的,因为我:: IntoIter 可能包含在里面(就像 Iter项目&LT任意寿命;'A,T&GT; 上述类型),以及我们在性状对象类型(否则寿命有关的信息将丢失)指定它们。

Here we hide the actual iterator type returned by filter() behind a trait object. Note that in order to make the function fully generic I had to add a lifetime parameter and a corresponding bound to Box trait object and I::IntoIter associated type. This is necessary because I::IntoIter may contain arbitrary lifetimes inside it (just like Iter<'a, T> type above), and we have to specify them in the trait object type (otherwise the lifetime information would be lost).

特质物体的Iterator 特质实施的Iterator 自己,这样你就可以继续使用这些迭代器像往常一样创建:

Trait objects created from Iterator trait implement Iterator themselves, so you can continue using these iterators as usual:

let source = vec![1_i32, 2, 3, 4];
for i in filter_even(source) {
    println!("{}", i);  // prints 2 and 4
}

这篇关于使用通用的迭代器,而不是具体名单类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆