Reputation: 4663
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
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))
}
Upvotes: 1