nikitablack
nikitablack

Reputation: 4663

How to fix `previous closure requires unique access` error?

I have the following code:

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

Which fails with the compilation error message:

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

I need to create a lot of objects where each one depends on another or on some other input parameters. I used scopeguard crate, but that's not important here - in the end, a closure is created which captures its environment and doesn't let me use the captured object.

As I understand, the s object is a mutable reference and the first borrow overlaps with the second borrow which is not allowed in Rust. I'm searching for a workaround or a completely different idiomatic way to solve my task.

Upvotes: 2

Views: 454

Answers (1)

user4815162342
user4815162342

Reputation: 154911

Given the constraints, I'd go with the obvious solution, a RefCell. It has a bad rap of being overused, but is actually designed for scenarios like this one. Using RefCell without an Rc is a good sign of doing it right.

RefCell allows the guard to capture the value by shared reference, which leaves it available for your use:

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

Upvotes: 1

Related Questions