【问题标题】:How to fix `previous closure requires unique access` error?如何修复“先前关闭需要唯一访问”错误?
【发布时间】:2021-09-28 10:45:37
【问题描述】:

我有以下代码:

use scopeguard::{guard, ScopeGuard}; // 1.1.0

struct S; // comes from a crate, non-clonable

impl S {
    fn free(&mut self, _: u32) {}
}

fn bar(_: &mut S) -> Result<(), ()> {
    Err(())
}

fn test(s: &mut S) -> Result<u32, ()> {
    let i = 10u32; // assuming this is an object from some crate, not just normal u32

    let sg = guard(i, |i| {
        s.free(i); // guarding the object. If something goes wrong the object should be freed.
    });
    
    let _ = bar(s)?; // assuming this can fail and the previously created object `i` should be freed before the function returns

    // here goes a lot of similar procedures:
    //   - first, create an object
    //   - second, wrap it in a guard closure
    // now if any of the procedures fail, all previously created objects will be freed
    
    Ok(ScopeGuard::into_inner(sg))
}

fn main() {
    let mut s = S {};

    let _ = test(&mut s);
}

编译错误消息失败:

error[E0501]: cannot borrow `*s` as mutable because previous closure requires unique access
  --> src/lib.rs:20:17
   |
16 |     let sg = guard(i, |i| {
   |                       --- closure construction occurs here
17 |         s.free(i); // guarding the object. Is something goes wrong the object should be freed.
   |         - first borrow occurs due to use of `*s` in closure
...
20 |     let _ = bar(s)?; // assuming this can fail and the previoously created object `i` should be freed before the function returns
   |                 ^ second borrow occurs here
21 | 
22 |     Ok(ScopeGuard::into_inner(sg))
   |                               -- first borrow later used here

我需要创建很多对象,其中每个对象都依赖于另一个对象或其他一些输入参数。我使用了scopeguard crate,但这在这里并不重要——最后,创建了一个闭包,它捕获了它的环境并且不允许我使用捕获的对象。

据我了解,s 对象是一个可变引用,第一个借用与第二个借用重叠,这在 Rust 中是不允许的。我正在寻找解决方法或完全不同的惯用方式来解决我的任务。

【问题讨论】:

  • @UlrichEckhardt 我知道如何使用搜索。您的链接没有回答如何以惯用方式解决我的特定任务的问题。
  • 范围保护需要对不相关变量s 进行可变访问似乎有点错误。什么是s,为什么作用域守卫需要改变它?
  • @SvenMarnach 在我的例子中svulkan::Allocator。分配器需要分配/释放 vulkan 内存,这就是它是可变引用的原因。 scopeguard 采用 FnOnce 类型的闭包,该闭包捕获 s
  • 考虑到限制,我会采用明显的解决方案,RefCell。这样,守卫只保留对单元格的共享引用,您可以继续使用它。 play.rust-lang.org/…

标签: rust


【解决方案1】:

考虑到这些限制,我会采用显而易见的解决方案,RefCell。它有一个被过度使用的坏名声,但实际上是为这样的场景设计的。使用 RefCell 而不使用 Rc 是正确使用的好兆头。

RefCell 允许守卫通过共享引用捕获值,从而使其可供您使用:

fn test(s: &mut S) -> Result<u32, ()> {
    let i = 10u32;
    let s = RefCell::new(s);

    let sg = guard(i, |i| {
        s.borrow_mut().free(i);
    });

    let _ = bar(*s.borrow_mut())?;

    Ok(ScopeGuard::into_inner(sg))
}

Playground

【讨论】:

    猜你喜欢
    • 2013-09-26
    • 1970-01-01
    • 2021-03-26
    • 1970-01-01
    • 1970-01-01
    • 2015-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多