【问题标题】:Borrowing errors whilst mutating in for loop在 for 循环中发生突变时借用错误
【发布时间】:2022-01-11 12:18:19
【问题描述】:

我在 rust 中遇到借用检查器和临时值问题。

我希望找到解决此特定问题的方法,并希望将来能更好地学习如何处理这种情况。我最初是从for_each 开始的,但遇到了提前终止的问题。

我考虑将check_foo 的逻辑移至update_foo,但这不适用于我的现实世界解决方案; MRE 侧重于编译问题与我在整个程序中试图实现的目标。


编辑:我有没有办法以纯功能的方式实现这一目标?


我想迭代一系列数字,更新 Vec<Foo> 并可能提前返回一个值。下面是我的代码具有相同错误的最小可重现示例:

我尝试尝试实现为:

fn run<'a>(mut foos: Vec<Foo>) -> Vec<&'a u32> {
    let mut bar: Vec<&u32> = vec![];

    for num in 0..10 {
        for foo in &mut foos {
            update_foo(foo);

            let checked_foo = check_foo(&foo);
            if checked_foo.is_empty() {
                bar = checked_foo;
                break;
            }
        }
    }

    bar
}

/* `fn update_foo` and `fn check_foo` definitions same as below */

但这导致:

21 | for foo in &mut foos {
   |            ^^^^^^^^^ `foos` was mutably borrowed here in the previous iteration of the loop

为了克服这个问题,我添加了 RcRefCell 的使用,以允许我在迭代引用的同时仍然能够变异:

#[derive(Clone, Debug, PartialEq)]
pub struct Foo {
    updated: bool,
}

fn run<'a>(foos: Vec<Rc<RefCell<Foo>>>) -> Vec<&'a u32> {
    let mut bar: Vec<&u32> = vec![];

    for num in 0..10 {
        for foo in &foos {
            update_foo(&mut foo.borrow_mut());

            let checked_foo = check_foo(&foo.borrow());
            if checked_foo.is_empty() {
                bar = checked_foo;
                break;
            }
        }
    }

    bar
}

fn update_foo(foo: &mut Foo) {
    foo.updated = true
}

fn check_foo(foo: &Foo) -> Vec<&u32> {
    if foo.updated {
        vec![&0, &1, &2]
    } else {
        vec![]
    }
}

导致:

error[E0515]: cannot return value referencing temporary value
  --> src/main.rs:33:5
   |
26 |             let checked_foo = check_foo(&foo.borrow());
   |                                          ------------ temporary value created here
...
33 |     bar
   |     ^^^ returns a value referencing data owned by the current function

error[E0515]: cannot return value referencing function parameter `foos`
  --> src/main.rs:33:5
   |
23 |         for foo in &foos {
   |                    ----- `foos` is borrowed here
...
33 |     bar
   |     ^^^ returns a value referencing data owned by the current function

For more information about this error, try `rustc --explain E0515`.

【问题讨论】:

  • 在您陷入困境试图解决错误之前,您能否发布您拥有的原始简单代码?
  • 嗨塞巴斯蒂安。我发布的是我的代码的 MRE。 check_foo 的等价物内部有更多的逻辑,但除此之外并没有太大的不同。 for foo in &amp;mut foos 是我在这个 fn 中的第一个错误,我在继续之前专注于解决。
  • 好的,这里有一些问题。只有当您有一些可以拥有数据并且比对它的引用的寿命更长的结构时,才应该返回引用。否则,您需要确保您传递的数据是拥有的。我假设您不想将foos 的所有权转移到run 函数,因为从那时起您将无法重用它。因此,这应该是一个参考。
  • 任何时候你返回一些新数据,这些数据没有指向来自传入引用的数据,你应该拥有它,这样它就可以在函数返回后继续存在。需要注意的是check_foo 的签名扩展为fn check_foo(foo: &amp;'a Foo) -&gt; Vec&lt;&amp;'a u32&gt; 这不是你想要的。此签名意味着您将 foo 限制为比您返回的向量的元素寿命更长。
  • bar 将是“引用当前函数拥有的数据”,因为借用检查器理解 check_foo 以返回对传入的 foo 的引用。因为 Vec @987654342 @ 归函数所有,它会在返回之前被删除,Rust 认为您正在返回对已删除的结构的引用。

标签: rust borrow-checker


【解决方案1】:

我不完全确定您打算如何处理此问题,但在我看来,您使用的一些参考资料应该拥有。这是我想出的。

#[derive(Clone, Debug, PartialEq)]
pub struct Foo {
    updated: bool,
}

fn run(foos: &mut Vec<Foo>) -> Vec<u32> {
    let mut bar: Vec<u32> = vec![];

    for num in 0..10 {
        for foo in foos.iter_mut() { 
            update_foo(foo);

            let checked_foo = check_foo(&foo);
            if checked_foo.is_empty() {
                bar = checked_foo;
                break;
            }
        }
    }

    bar
}

fn update_foo(foo: &mut Foo) {
    foo.updated = true
}

fn check_foo(foo: &Foo) -> Vec<u32> {
    if foo.updated {
        vec![0, 1, 2]
    } else {
        vec![]
    }
}

当您希望其他结构拥有您所引用的对象时,应该使用引用,但在这里您正在使用新数据构造新向量,因此您应该保留元素的所有权。

【讨论】:

  • 嗨马塞尔。感谢您的回复。我刚刚在bar = checked_foo; 之后用缺少的break; 更新了我的帖子,希望能阐明我想要实现的目标。本质上,我想搜索foo,更新它们并最终提前返回。在我的 MRE 中,这可能不清楚。
  • 你为什么要这样做 10 次?你的意思是要打破外循环吗?
  • 在我的真实代码中,我正在迭代 u32,我只是将 10 用于示例。逻辑本身并没有在这里全部实现,因为我对问题的关注只是编译错误。
  • 您的解决方案看起来不错。您能否详细说明我做错了什么以及您的解决方案如何解决它?我仍然不确定您的解决方案如何能够迭代 foos
  • 如果您可以在添加 RcRefCell 之前发布原始代码,我可以解释发生了什么问题。
猜你喜欢
  • 1970-01-01
  • 2018-11-04
  • 2017-01-30
  • 2019-08-12
  • 1970-01-01
  • 1970-01-01
  • 2019-12-06
  • 2018-10-11
  • 1970-01-01
相关资源
最近更新 更多