我可以编写一个迭代器来改变自身然后产生一个对自身的引用吗? [英] Can I write an Iterator that mutates itself and then yields a reference into itself?

查看:35
本文介绍了我可以编写一个迭代器来改变自身然后产生一个对自身的引用吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

struct MyIter {vec:Vec i8 ,}fn fill_with_useful_data(v: &mut Vec<i8>) {/* ... */}impl'a>MyIter 的迭代器 {输入项目 = &'a [i8];fn next(&mut self) ->选项<&'a [i8]>{fill_with_useful_data(&mut self.vec);一些(&self.vec)}}fn 主(){对于切片 (MyIter { vec: Vec::new() }) {println!("{}", 切片);}}

这会产生错误:

error[E0207]: 生命周期参数 `'a` 不受 impl trait、self 类型或谓词的约束-->src/main.rs:9:6|9 |impl'a>MyIter 的迭代器 {|^^ 无约束生命周期参数

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

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

我阅读了迭代器产生可变引用"博文,但我不确定它是否/如何适用于我的不涉及可变引用的问题.

解决方案

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

为了让编译器对此进行推理并允许将引用返回到 self 接下来需要像

这样的签名

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

然而,这与 trait 的签名不同,后者不允许作为通用代码,只需要一个 T: Iterator<...> 无法判断使用上有不同的要求某些 T 的返回值;所有都必须以相同的方式处理.

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

I ran into a problem that simplifies into the following:

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

This generates the error:

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

The idea is that the iterator does a bunch of work that reflects in its fields and at each step, it yields a reference into itself to the calling code. In this case I could model it as yielding a copy of the state instead of the reference, but let's pretend that's not possible or just inconveniently expensive.

Intuitively this shouldn't be a problem because the borrow checker can ensure that .next() isn't called again while the yielded reference can still be used to inspect the iterator's state, but the Iterator trait doesn't seem to provide for that sort of thing directly. Even with some permutations like only holding on to a reference to the vector in the iterator itself or making the iterator a reference or something to get the lifetimes baked into the type earlier on, I can't get anything past the borrow checker.

I read the "Iterators yielding mutable references" blogpost but I'm not sure if/how it applies to my problem that doesn't involve mutable references.

解决方案

This is not possible. If it were allowed one could call next again and thus modify data that is also visible via & or even invalidate the reference entirely. This is because there is no connection between the self object itself and the returned reference: there is no explicit lifetime linking them.

For the compiler to reason about this and allow returning a reference into self next needs a signature like

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

However, this differs from the signature of the trait which is not allowed as generic code that just takes an T: Iterator<...> cannot tell that there are different requirements on the use of the return value for some T; all have to be handled identically.

The Iterator trait is designed for return values that are independent of the iterator object, which is necessary for iterator adaptors like .collect to be correct and safe. This is more restrictive than necessary for many uses (e.g. a transient use inside a for loop) but it is just how it is at the moment. I don't think we have the tools for generalising this trait/the for loop properly now (specifically, I think we need associated types with higher rank lifetimes), but maybe in the future.

这篇关于我可以编写一个迭代器来改变自身然后产生一个对自身的引用吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆