【问题标题】:Using other methods on a structure inside a `for` loop over an iterator which can mutate this structure在可以改变这个结构的迭代器上的“for”循环内对结构使用其他方法
【发布时间】:2014-01-03 14:54:45
【问题描述】:

我在结构上有类似以下方法:

impl<'a> SomeStructure<'a> {
    // I need &'a mut because the iterator may mutate SomeStructure
    fn iter<'a>(&'a mut self) -> SomeIterator<'a> {
        SomeIterator { object: self }
    }

    fn test_something(&self) -> bool {
        self.some_field < 0
    }

}

然后我想这样使用它们:

impl<'a> SomeTrait for &'a mut SomeStructure<'a> {
    fn do_something(self) {
        for e in self.iter() {
            ...
            if self.test_something() {
                break;
            }
        }
    }
}

但是,Rust 不允许这样做(我已经修复了错误消息,因此它们参考了上面的示例代码):

io/convert_io.rs:119:17: 119:22 error: cannot borrow `*self` as immutable because it is also borrowed as mutable
io/convert_io.rs:119             if self.test_something() {
                                    ^~~~~
io/convert_io.rs:117:18: 117:23 note: previous borrow of `*self` occurs here
io/convert_io.rs:117         for e in self.iter() {
                                      ^~~~~

但我看不出self.test_something() 调用中的不可变借用如何干扰更早创建迭代器,即使迭代器确实改变了原始对象。

您能否解释一下这里发生了什么以及如何解决它?

【问题讨论】:

  • 这真的是 Rust 的样子吗?哎呀!

标签: pointers iterator rust


【解决方案1】:

这可能是由当前for 的最小工作实现引起的错误#8372。它被实现为一个宏,使得

for pattern in iterator { body }

扩展为(您可以通过运行 rustc --pretty expanded foo.rs 看到这一点)

{
    let it = &mut iterator;
    loop {
        match it.next() {
            None => break,
            Some(pattern) => { body }
        }
    }
}

问题在于&amp;mut iterator 借用,当it 在范围内时,它会停止直接使用iterator。您通常可以通过自己手动编写扩展来解决此问题:

impl<'a> SomeTrait for &'a mut SomeStructure<'a> {
    fn do_something(self) {
        let mut it = self.iter();
        loop {
            match it.next() {
                None => break
                Some(e) => {
                    if self.test_something() {
                        break;
                    }
                }
            }
        }
    }
}

也就是说...如果self.iter() 借用self(尤其是如果test_something 采用&amp;mut self,因为编译器必须禁止突变或迭代器可能无效),这实际上可能不起作用。

如果您要在 self.iter() 的返回值中添加 .view_creator() 方法(假设您在 self.iter() 类型中引用了 self),那么 it.view_creator().test_something() 将起作用(使用手动解包的 for 循环)。

(FWIW,有一个特征采用 self 并在 &amp;mut Thing 上实现它而不是采用 &amp;mut self 并直接在 Thing 上实现它有点奇怪;尽管有 有时有充分的理由。)

【讨论】:

  • 是的,我尝试手动解开循环,但没有成功。顺便说一句,您似乎给出了错误的错误 ID :) 是的,我要实现这样的特征是有原因的。从示例代码中可以看到SomeTrait 没有任何生命周期参数,但SomeStructure 有,并且SomeStructure 上也有方法接受&amp;'a 指针。但是你不能定义采用&amp;'a [mut] self的trait方法,没有这个就不可能在SomeStructure上调用相应的方法。
  • 也感谢您的建议。我会试一试。令人遗憾的是,在 Rust 中不可能做相当理智的事情(希望,还没有)。
  • 我认为你实际上是指 match it.next()for 循环扩展:)
  • Rust 中相当理智的东西 一般来说,它并不理智;并且编译器很难将这种(可能)合理的情况与其他实际无效的情况区分开来(其中一个人正在使用不可变的&amp;借用和&amp;mut借用,并通过通过&amp;mut进行修改使第一个无效一)你可以声明像fn foo&lt;'a&gt;(&amp;'a mut self) -&gt; &amp;'a ReturnValue,fwiw这样的特征方法,但是,正如你所说,这并不总是有效的。
  • (你对我的两个错误都是正确的,我很抱歉。(已修复))无论如何,我认为 it.object.test_something() 应该可以。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多