【问题标题】:Iterator returning a reference to itself迭代器返回对自身的引用
【发布时间】:2017-03-20 06:04:35
【问题描述】:

我知道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 的源代码,这是一个很好的例子。但是,在我的情况下,我想在每次调用 next() 时改变 Vec。这可能吗?

【问题讨论】:

    标签: iterator rust lifetime


    【解决方案1】:

    正如弗朗西斯所提到的,在迭代期间不可能修改底层向量。但是,如果您能够以某种方式指定迭代界限,那么事情会容易得多:

    • 您将创建矢量[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&lt;Vec&lt;Box&lt;[T]&gt;&gt;&gt; 之类的东西。

    【讨论】:

    • 谢谢。你的答案有一些有用的东西。如果我能同时接受这两个答案,我会接受,但我认为弗朗西斯的回答更侧重于我的两个问题。
    【解决方案2】:

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

    您尝试归还的借用没有生命周期'a,正如承诺的那样。相反,它与self 具有相同的生命周期。如果next的签名是完整的,那就是:

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

    如果不是因为它违反了 Iterator 特征的约定,则返回 Option&lt;&amp;'b [usize]&gt;(生命周期为 'b 而不是 'a)将是有效的。但是,它会冻结self,直到结果被删除;即您不能两次调用next 并同时使用两次调用的结果。这是因为每次调用next 都可能使之前返回的切片失效;推送到 Vec 可以重新定位内存中的存储空间,以便为其他元素腾出空间,因此切片中的指针将不再有效。

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

    使用 Iterator 特征无法做到这一点,因此您将无法在结构上使用 for 循环。但是,您可以使用普通方法来完成(需要注意上述警告)。

    struct Countings(Vec<usize>);
    
    impl Countings {
        fn new() -> Countings { Countings(vec![]) }
    
        fn next<'a>(&'a mut self) -> &'a [usize] {
            let item = self.0.len();
            self.0.push(item);
            self.0.as_slice()
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2016-02-16
      • 1970-01-01
      • 1970-01-01
      • 2017-12-10
      • 1970-01-01
      相关资源
      最近更新 更多