简体   繁体   English

如何编写一个返回对自身的引用的迭代器?

[英]How do I write an iterator that returns references to itself?

I am having trouble expressing the lifetime of the return value of an Iterator implementation.我无法表达Iterator实现的返回值的生命周期。 How can I compile this code without changing the return value of the iterator?如何在不更改迭代器返回值的情况下编译此代码? I'd like it to return a vector of references.我希望它返回一个引用向量。

It is obvious that I am not using the lifetime parameter correctly but after trying various ways I just gave up, I have no idea what to do with it.很明显,我没有正确使用生命周期参数,但是在尝试了各种我放弃的方法之后,我不知道该怎么处理它。

use std::iter::Iterator;

struct PermutationIterator<T> {
    vs: Vec<Vec<T>>,
    is: Vec<usize>,
}

impl<T> PermutationIterator<T> {
    fn new() -> PermutationIterator<T> {
        PermutationIterator {
            vs: vec![],
            is: vec![],
        }
    }

    fn add(&mut self, v: Vec<T>) {
        self.vs.push(v);
        self.is.push(0);
    }
}

impl<T> Iterator for PermutationIterator<T> {
    type Item = Vec<&'a T>;
    fn next(&mut self) -> Option<Vec<&T>> {
        'outer: loop {
            for i in 0..self.vs.len() {
                if self.is[i] >= self.vs[i].len() {
                    if i == 0 {
                        return None; // we are done
                    }
                    self.is[i] = 0;
                    self.is[i - 1] += 1;
                    continue 'outer;
                }
            }

            let mut result = vec![];

            for i in 0..self.vs.len() {
                let index = self.is[i];
                result.push(self.vs[i].get(index).unwrap());
            }

            *self.is.last_mut().unwrap() += 1;

            return Some(result);
        }
    }
}

fn main() {
    let v1: Vec<_> = (1..3).collect();
    let v2: Vec<_> = (3..5).collect();
    let v3: Vec<_> = (1..6).collect();

    let mut i = PermutationIterator::new();
    i.add(v1);
    i.add(v2);
    i.add(v3);

    loop {
        match i.next() {
            Some(v) => {
                println!("{:?}", v);
            }
            None => {
                break;
            }
        }
    }
}

( Playground link ) 游乐场链接

error[E0261]: use of undeclared lifetime name `'a`
  --> src/main.rs:23:22
   |
23 |     type Item = Vec<&'a T>;
   |                      ^^ undeclared lifetime

As far as I understand, you want want the iterator to return a vector of references into itself, right?据我了解,您希望迭代器将引用向量返回到自身中,对吗? Unfortunately, it is not possible in Rust.不幸的是,这在 Rust 中是不可能的。

This is the trimmed down Iterator trait:这是精简的Iterator特征:

trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Item>;
}

Note that there is no lifetime connection between &mut self and Option<Item> .请注意, &mut selfOption<Item>之间没有生命周期连接 This means that next() method can't return references into the iterator itself.这意味着next()方法不能将引用返回到迭代器本身。 You just can't express a lifetime of the returned references.您只是无法表达返回引用的生命周期。 This is basically the reason that you couldn't find a way to specify the correct lifetime - it would've looked like this:这基本上是您找不到指定正确生命周期的方法的原因 - 它看起来像这样:

fn next<'a>(&'a mut self) -> Option<Vec<&'a T>>

except that this is not a valid next() method for Iterator trait.除了这不是Iterator trait 的有效next()方法。

Such iterators (the ones which can return references into themselves) are called streaming iterators .这样的迭代器(可以将引用返回给自身的迭代器)称为流迭代器 You can find more here , here and here , if you want.如果需要,您可以在此处此处此处找到更多信息

Update.更新。 However, you can return a reference to some other structure from your iterator - that's how most of collection iterators work.但是,您可以从迭代器返回对其他结构的引用 - 这就是大多数集合迭代器的工作方式。 It could look like this:它可能看起来像这样:

pub struct PermutationIterator<'a, T> {
    vs: &'a [Vec<T>],
    is: Vec<usize>
}

impl<'a, T> Iterator for PermutationIterator<'a, T> {
    type Item = Vec<&'a T>;

    fn next(&mut self) -> Option<Vec<&'a T>> {
        ...
    }
}

Note how lifetime 'a is now declared on impl block.请注意生命周期'a现在是如何在impl块上声明的。 It is OK to do so (required, in fact) because you need to specify the lifetime parameter on the structure.这样做是可以的(实际上是必需的),因为您需要在结构上指定生命周期参数。 Then you can use the same 'a both in Item and in next() return type.然后您可以在Itemnext()返回类型中使用相同的'a Again, that's how most of collection iterators work.同样,这就是大多数集合迭代器的工作方式。

@VladimirMatveev's answer is correct in how it explains why your code cannot compile. @VladimirMatveev 的答案是正确的,它解释了为什么您的代码无法编译。 In a nutshell, it says that an Iterator cannot yield borrowed values from within itself.简而言之,它表示迭代器不能从自身内部产生借用的值。

However, it can yield borrowed values from something else.但是,它可以从其他东西中产生借来的价值。 This is what is achieved with Vec and Iter : the Vec owns the values, and the the Iter is just a wrapper able to yield references within the Vec .这就是使用VecIter实现的: Vec拥有值,而Iter只是一个包装器,能够在Vec产生引用。

Here is a design which achieves what you want.这是一个可以实现您想要的设计。 The iterator is, like with Vec and Iter , just a wrapper over other containers who actually own the values.迭代器与VecIter ,只是对实际拥有值的其他容器的包装器。

use std::iter::Iterator;

struct PermutationIterator<'a, T: 'a> {
    vs : Vec<&'a [T]>,
    is : Vec<usize>
}

impl<'a, T> PermutationIterator<'a, T> {
    fn new() -> PermutationIterator<'a, T> { ... }

    fn add(&mut self, v : &'a [T]) { ... }
}

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

fn main() {
    let v1 : Vec<i32> = (1..3).collect();
    let v2 : Vec<i32> = (3..5).collect();
    let v3 : Vec<i32> = (1..6).collect();

    let mut i = PermutationIterator::new();
    i.add(&v1);
    i.add(&v2);
    i.add(&v3);

    loop {
        match i.next() {
            Some(v) => { println!("{:?}", v); }
            None => {break;}
        }
    }
}

(Playground) (操场)


Unrelated to your initial problem.与您最初的问题无关。 If this were just me, I would ensure that all borrowed vectors are taken at once.如果这只是我,我会确保一次性获取所有借用的向量。 The idea is to remove the repeated calls to add and to pass directly all borrowed vectors at construction:这个想法是删除重复调用add并在构造时直接传递所有借用的向量:

use std::iter::{Iterator, repeat};

struct PermutationIterator<'a, T: 'a> {
    ...
}

impl<'a, T> PermutationIterator<'a, T> {
    fn new(vs: Vec<&'a [T]>) -> PermutationIterator<'a, T> {
        let n = vs.len();
        PermutationIterator {
            vs: vs,
            is: repeat(0).take(n).collect(),
        }
    }
}

impl<'a, T> Iterator for PermutationIterator<'a, T> {
    ...
}

fn main() {
    let v1 : Vec<i32> = (1..3).collect();
    let v2 : Vec<i32> = (3..5).collect();
    let v3 : Vec<i32> = (1..6).collect();
    let vall: Vec<&[i32]> = vec![&v1, &v2, &v3];

    let mut i = PermutationIterator::new(vall);
}

(Playground) (操场)

( EDIT : Changed the iterator design to take a Vec<&'a [T]> rather than a Vec<Vec<&'a T>> . It's easier to take a ref to container than to build a container of refs.) 编辑:更改迭代器设计以采用Vec<&'a [T]>而不是Vec<Vec<&'a T>> 。将引用带到容器比构建引用容器更容易。)

As mentioned in other answers, this is called a streaming iterator and it requires different guarantees from Rust's Iterator .正如其他答案中提到的,这称为流迭代器,它需要与 Rust 的Iterator不同的保证。 One crate that provides such functionality is aptly called streaming-iterator and it provides the StreamingIterator trait.一个提供这种功能的 crate 被恰当地称为流迭代器,它提供了StreamingIterator特性。

Here is one example of implementing the trait:这是实现 trait 的一个例子:

extern crate streaming_iterator;

use streaming_iterator::StreamingIterator;

struct Demonstration {
    scores: Vec<i32>,
    position: usize,
}

// Since `StreamingIterator` requires that we be able to call
// `advance` before `get`, we have to start "before" the first
// element. We assume that there will never be the maximum number of
// entries in the `Vec`, so we use `usize::MAX` as our sentinel value.
impl Demonstration {
    fn new() -> Self {
        Demonstration {
            scores: vec![1, 2, 3],
            position: std::usize::MAX,
        }
    }

    fn reset(&mut self) {
        self.position = std::usize::MAX;
    }
}

impl StreamingIterator for Demonstration {
    type Item = i32;

    fn advance(&mut self) {
        self.position = self.position.wrapping_add(1);
    }

    fn get(&self) -> Option<&Self::Item> {
        self.scores.get(self.position)
    }
}

fn main() {
    let mut example = Demonstration::new();

    loop {
        example.advance();
        match example.get() {
            Some(v) => {
                println!("v: {}", v);
            }
            None => break,
        }
    }

    example.reset();

    loop {
        example.advance();
        match example.get() {
            Some(v) => {
                println!("v: {}", v);
            }
            None => break,
        }
    }
}

Unfortunately, streaming iterators will be limited until generic associated types (GATs) from RFC 1598 are implemented.不幸的是,在实现 RFC 1598 中的通用关联类型(GAT)之前,流迭代器将受到限制。

I wrote this code not long ago and somehow stumbled on this question here.我不久前写了这段代码,不知何故在这里偶然发现了这个问题。 It does exactly what the question asks: it shows how to implement an iterator that passes its callbacks a reference to itself.它完全符合问题的要求:它展示了如何实现一个迭代器,将其回调传递给自身的引用。

It adds an .iter_map() method to IntoIterator instances.它向IntoIterator实例添加了一个.iter_map()方法。 Initially I thought it should be implemented for Iterator itself, but that was a less flexible design decision.最初我认为它应该为Iterator本身实现,但这是一个不太灵活的设计决策。

I created a small crate for it and posted my code to GitHub in case you want to experiment with it, you can find it here .我为它创建了一个小板条箱,并将我的代码发布到 GitHub,以防您想尝试使用它,您可以在此处找到它

WRT the OP's trouble with defining lifetimes for the items, I didn't run into any such trouble implementing this while relying on the default elided lifetimes. WRT OP 在为项目定义生命周期方面遇到了麻烦,我在依赖默认省略的生命周期时没有遇到任何此类问题。

Here's an example of usage.这是一个使用示例。 Note the parameter the callback receives is the iterator itself, the callback is expected to pull the data from it and either pass it along as is or do whatever other operations.请注意,回调接收的参数是迭代器本身,回调预计会从中提取数据并按原样传递或执行任何其他操作。

 use iter_map::IntoIterMap;

 let mut b = true;

 let s = "hello world!".chars().peekable().iter_map(|iter| {
     if let Some(&ch) = iter.peek() {
         if ch == 'o' && b {
             b = false;
             Some('0')
         } else {
             b = true;
             iter.next()
         }
     } else { None }
 }).collect::<String>();

 assert_eq!(&s, "hell0o w0orld!");

Because the IntoIterMap generic trait is implemented for IntoIterator , you can get an "iter map" off anything that supports that interface.因为IntoIterMap通用特征是为IntoIterator实现的, IntoIterator您可以从支持该接口的任何东西中获得“迭代映射”。 For instance, one can be created directly off an array, like so:例如,可以直接从数组中创建一个,如下所示:

use iter_map::*;

fn main() 
{
    let mut i = 0;

    let v = [1, 2, 3, 4, 5, 6].iter_map(move |iter| {
        i += 1;
        if i % 3 == 0 {
            Some(0)
        } else {
            iter.next().copied()
        }
    }).collect::<Vec<_>>();
 
    assert_eq!(v, vec![1, 2, 0, 3, 4, 0, 5, 6, 0]);
}

Here's the full code - it was amazing it took such little code to implement, and everything just seemed to work smoothly while putting it together.这是完整的代码 - 用这么少的代码来实现真是太神奇了,而且在将它们组合在一起时,一切似乎都运行得很顺利。 It gave me a new appreciation for the flexibility of Rust itself and its design decisions.它让我对 Rust 本身的灵活性及其设计决策有了新的认识。

/// Adds `.iter_map()` method to all IntoIterator classes.
///
impl<F, I, J, R, T> IntoIterMap<F, I, R, T> for J
//
where F: FnMut(&mut I) -> Option<R>,
      I: Iterator<Item = T>,
      J: IntoIterator<Item = T, IntoIter = I>,
{
    /// Returns an iterator that invokes the callback in `.next()`, passing it
    /// the original iterator as an argument. The callback can return any
    /// arbitrary type within an `Option`.
    ///
    fn iter_map(self, callback: F) -> ParamFromFnIter<F, I>
    {
        ParamFromFnIter::new(self.into_iter(), callback)
    }
}

/// A trait to add the `.iter_map()` method to any existing class.
///
pub trait IntoIterMap<F, I, R, T>
//
where F: FnMut(&mut I) -> Option<R>,
      I: Iterator<Item = T>,
{
    /// Returns a `ParamFromFnIter` iterator which wraps the iterator it's 
    /// invoked on.
    ///
    /// # Arguments
    /// * `callback`  - The callback that gets invoked by `.next()`.
    ///                 This callback is passed the original iterator as its
    ///                 parameter.
    ///
    fn iter_map(self, callback: F) -> ParamFromFnIter<F, I>;
}

/// Implements an iterator that can be created from a callback.
/// does pretty much the same thing as `std::iter::from_fn()` except the 
/// callback signature of this class takes a data argument.
pub struct ParamFromFnIter<F, D>
{
    callback: F,
    data: D,
}

impl<F, D, R> ParamFromFnIter<F, D>
//
where F: FnMut(&mut D) -> Option<R>,
{
    /// Creates a new `ParamFromFnIter` iterator instance.
    ///
    /// This provides a flexible and simple way to create new iterators by 
    /// defining a callback. 
    /// # Arguments
    /// * `data`      - Data that will be passed to the callback on each 
    ///                 invocation.
    /// * `callback`  - The callback that gets invoked when `.next()` is invoked
    ///                 on the returned iterator.
    ///    
    pub fn new(data: D, callback: F) -> Self
    {
        ParamFromFnIter { callback, data }
    }
}

/// Implements Iterator for ParamFromFnIter. 
///
impl<F, D, R> Iterator for ParamFromFnIter<F, D>
//
where F: FnMut(&mut D) -> Option<R>,
{
    type Item = R;
    
    /// Iterator method that returns the next item.
    /// Invokes the client code provided iterator, passing it `&mut self.data`.
    ///
    fn next(&mut self) -> Option<Self::Item>
    {
        (self.callback)(&mut self.data)
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM