迭代器返回对自身的引用

时间:2016-11-07 00:02:10

标签: iterator rust lifetime

我知道Lifetime in Iterator impl,但我想了解更多细节以帮助我正确理解。

我想要写一个返回Iterator&[0]&[0, 1]等的无限&[0, 1, 2]。我想写这个:

struct Countings(Vec<usize>);

impl Countings {
    fn new() -> Countings { Countings(vec![]) }
}

impl Iterator for Countings {
    type Item = &[usize];

    fn next(&mut self) -> Option<Self::Item> {
        self.0.push(self.0.len());
        Some(self.0.as_slice())
    }
}

我不能,因为类型Countings::Item没有生命周期。

error[E0106]: missing lifetime specifier
 --> src/lib.rs:8:17
  |
8 |     type Item = &[usize];
  |                 ^ expected lifetime parameter

所以我加一个。它必须受impl Iterator的约束。反过来,这需要struct Countings上的生命周期参数。到目前为止,我在这里:

struct Countings<'a>(Vec<usize>);

impl<'a> Countings<'a> {
    fn new() -> Countings<'a> { Countings(vec![]) }
}

impl<'a> Iterator for Countings<'a> {
    type Item = &'a [usize];

    fn next(&mut self) -> Option<Self::Item> {
        self.0.push(self.0.len());
        Some(self.0.as_slice())
    }
}

现在我有一个不同的错误:

error[E0392]: parameter `'a` is never used
 --> src/lib.rs:1:18
  |
1 | struct Countings<'a>(Vec<usize>);
  |                  ^^
  |
  = help: consider removing `'a` or using a marker such as `std::marker::PhantomData`

我认真考虑过:

use std::marker::PhantomData;

struct Countings<'a>(Vec<usize>, PhantomData<&'a [usize]>);

impl<'a> Countings<'a> {
    fn new() -> Countings<'a> { Countings(vec![], PhantomData) }
}

impl<'a> Iterator for Countings<'a> {
    type Item = &'a [usize];

    fn next(&mut self) -> Option<Self::Item> {
        self.0.push(self.0.len());
        Some(self.0.as_slice())
    }
}

但无济于事:

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/lib.rs:14:25
   |
14 |             Some(self.0.as_slice())
   |                         ^^^^^^^^

问题1:“冲突要求”是什么?

问题2:answer cited above表示Item必须借用Iterator包装的内容。我已经阅读了std::slice::Windows的来源,这是一个很好的例子。但是,在我的情况下,我希望每次调用Vec时都要改变next()。这可能吗?

2 个答案:

答案 0 :(得分:2)

  

问题1:&#34;冲突的要求是什么&#34;?

您尝试返回的借款没有承诺的终身'a。相反,它与self具有相同的生命周期。如果next的签名是完整的,那就是:

fn next<'b>(&'b mut self) -> Option<&'a [usize]>

如果违反{{1}的合同,则返回Option<&'b [usize]>(有效期为'b而不是'a)将有效。特质。但是,它会冻结Iterator,直到结果被删除;即你不能两次拨打self并同时使用两个电话的结果。这是因为每次调用next都可能使先前返回的切片无效;推送到next可以重新定位内存中的存储空间以便为其他元素腾出空间,因此切片中的指针将不再有效。

  

问题2:answer cited above表示Vec必须借用Item包装的内容。我已经阅读了std::slice::Windows的来源,这是一个很好的例子。但是,在我的情况下,我希望每次调用Iterator时都要改变Vec。这可能吗?

使用next()特征无法做到这一点,因此您无法在结构上使用Iterator循环。但是,您可以使用普通方法(上面提到的警告)来做到这一点。

for

答案 1 :(得分:2)

正如弗朗西斯所提到的,在迭代过程中无法修改基础向量。但是,如果你以某种方式有可能指定迭代界限,那么事情会容易得多:

  • 您将创建向量[0, 1, 2, ...]
  • 然后创建一个迭代器,返回一个不断增长的切片,直到向量的长度

只是迭代器:

struct EverGrowingIterator<'a, T: 'a> {
    slice: &'a [T],
    current: usize,
}

impl<'a, T> Iterator for EverGrowingIterator<'a, T> {
    type Item = &'a [T];

    fn next(&mut self) -> Option<&'a [T]> {
        if self.current >= self.slice.len() {
            None
        } else {
            self.current += 1;
            Some(&self.slice[0..self.current])
        }
    }
}

然后:

fn ever_growing<'a, T>(slice: &'a [T]) -> EverGrowingIterator<'a, T> {
    EverGrowingIterator { slice: slice, current: 0 }
}

fn main() {
    let v = vec![0, 1, 2];
    for s in ever_growing(&v) {
        println!("{:?}", s);
    }
}

将打印:

[0]
[0, 1]
[0, 1, 2]

如果您需要对此进行调整以实现无限增长,则需要考虑创建一个自定义容器(不是Vec),该容器将增长,同时保留对先前切片的引用。可以使用RefCell<Vec<Box<[T]>>>之类的东西。