web-dev-qa-db-ja.com

自分自身への参照を返すイテレータをどのように書くのですか?

Iterator実装の戻り値の有効期間を表現するのに問題があります。イテレータの戻り値を変更せずにこのコードをコンパイルするにはどうすればよいですか?参照のベクトルを返したいのですが。

ライフタイムパラメータを正しく使用していないことは明らかですが、あきらめたばかりのさまざまな方法を試した後、どうすればよいかわかりません。

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;
            }
        }
    }
}

遊び場リンク

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

私が理解している限り、イテレータが参照のベクトルをそれ自体に返すことを望んでいますよね?残念ながら、Rustでは不可能です。

これは、縮小されたIterator特性です:

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

_&mut self_と_Option<Item>_の間に有効期間の接続がないことに注意してください。つまり、next()メソッドは、イテレータ自体への参照を返すことができません。返された参照の有効期間を表現することはできません。これが基本的に、正しいライフタイムを指定する方法を見つけられなかった理由です。次のようになります。

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

ただし、これはIterator特性に対して有効なnext()メソッドではありません。

そのようなイテレータ(自分自身に参照を返すことができるもの)はストリーミングイテレータと呼ばれます。必要に応じて、さらに ここここ および ここ を見つけることができます。

Update。ただし、イテレータから他の構造への参照を返すことができます。これがコレクションイテレータのほとんどの動作方法です。次のようになります。

_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>> {
        ...
    }
}
_

ライフタイム_'a_がimplブロックで宣言されていることに注意してください。構造に寿命パラメーターを指定する必要があるため、そうすることは問題ありません(実際には必須です)。次に、Itemnext()の両方の戻り値型で同じ_'a_を使用できます。繰り返しになりますが、これがほとんどのコレクション反復子の動作方法です。

24

@ VladimirMatveevの答え は、コードがコンパイルできない理由理由の説明が正しいです。一言で言えば、それはイテレータがそれ自体から借りた値を生み出すことができないと言います。

ただし、それは他の何かから借用した値を生み出す可能性があります。これは、VecIterで実現されるものです。Vecは値を所有し、Iterは、 Vec

ここにあなたが望むものを達成するデザインがあります。イテレータは、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;}
        }
    }
}

(プレイグラウンド)


最初の問題とは無関係です。これが私だけの場合、借りたすべてのベクトルが一度に取得されるようにします。アイデアは、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);
}

(プレイグラウンド)

[〜#〜] edit [〜#〜]:イテレータのデザインを変更してVec<&'a [T]>ではなくVec<Vec<&'a T>>。参照のコンテナーを作成するよりも、コンテナーに参照を取得する方が簡単です。)

6
mdup

他の回答で述べたように、これはストリーミングイテレータと呼ばれ、RustのIteratorとは異なる保証が必要です。そのような機能を提供する1つのクレートは、適切に streaming-iterator と呼ばれ、 StreamingIterator 特性を提供します。

以下は、トレイトを実装する1つの例です。

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,
        }
    }
}

残念ながら、ストリーミングイテレータは、RFC 1598の generic associated types(GATs) が実装されるまで制限されます。

2
Shepmaster