【问题标题】:Can I write an Iterator that mutates itself and then yields a reference into itself?我可以编写一个自身发生变异然后产生对自身的引用的迭代器吗?
【发布时间】:2014-10-31 09:54:04
【问题描述】:

我遇到了一个简化为以下内容的问题:

struct MyIter {
    vec: Vec<i8>,
}

fn fill_with_useful_data(v: &mut Vec<i8>) {
    /* ... */
}

impl<'a> Iterator for MyIter {
    type Item = &'a [i8];

    fn next(&mut self) -> Option<&'a [i8]> {
        fill_with_useful_data(&mut self.vec);

        Some(&self.vec)
    }
}

fn main() {
    for slice in (MyIter { vec: Vec::new() }) {
        println!("{}", slice);
    }
}

这会产生错误:

error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
 --> src/main.rs:9:6
  |
9 | impl<'a> Iterator for MyIter {
  |      ^^ unconstrained lifetime parameter

这个想法是,迭代器做了一堆反映在其字段中的工作,并且在每一步,它都会产生对调用代码的自身引用。在这种情况下,我可以将其建模为生成状态副本而不是引用,但让我们假设这是不可能的,或者只是代价高昂。

直觉上这应该不是问题,因为借用检查器可以确保不会再次调用.next(),而产生的引用仍可用于检查迭代器的状态,但Iterator trait 似乎没有直接提供这种东西。即使有一些排列,比如只在迭代器本身中保留对向量的引用,或者使迭代器成为引用或其他东西,以便更早地将生命周期烘焙到类型中,我无法通过借用检查器获得任何东西。

我阅读了“Iterators yielding mutable references”博文,但我不确定它是否/如何适用于我的不涉及可变引用的问题。

【问题讨论】:

    标签: rust


    【解决方案1】:

    这是不可能的。如果允许,可以再次调用next,从而修改通过&amp; 也可见的数据,甚至使引用完全无效。这是因为self 对象本身和返回的引用之间没有联系:没有明确的生命周期链接它们。

    为了让编译器对此进行推理并允许将引用返回到self,接下来需要一个类似的签名

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

    然而,这与特征的签名不同,这是不允许的,因为通用代码只接受T: Iterator&lt;...&gt; 不能告诉对某些T 的返回值的使用有不同的要求;都必须以相同的方式处理。

    Iterator trait 是为独立于迭代器对象的返回值而设计的,这对于像 .collect 这样的迭代器适配器来说是正确和安全的。对于许多用途(例如,在for 循环内的临时使用)来说,这比必要的限制更多,但目前就是这样。我认为我们现在还没有工具来正确概括这个特征/for 循环(具体来说,我认为我们需要具有更高等级生命周期的关联类型),但也许在未来。

    【讨论】:

    • 好吧,我刚刚停止实现Iterator 并使用宏创建了自己的for循环。没想到.collect() 怎么会不行。
    • 这对于不能使用 for 循环的遍历也很好:while let Some(element) = iter.next() { ...
    • 为了将来参考,这种类型的迭代器 (fn next(&amp;'a self)) 通常被称为 流式迭代器12crate that implements a form of it 的一些讨论示例。
    猜你喜欢
    • 2016-02-16
    • 2021-03-28
    • 2017-03-20
    • 1970-01-01
    • 2017-01-13
    相关资源
    最近更新 更多