How can I modify captured `self` in closure?


#1

Hi, I’m a newbie struggling to fight borrow checker. Is it possible to compile the code similar to the following methods in C++ and C#:

void ModifyField()
{
    // this->field_ = 1729;
    auto deferredAssignment = [&] { this->field_ = 1729; };
    deferredAssignment();
    this->field_ = 19937;
}
internal void ModifyField()
{
    // this._field = 1729;
    Action deferredAssignment = () => { this._field = 1729; };
    deferredAssignment();
    this._field = 19937;
}

What I’m trying to do inside struct impl is:

fn modify_field(&mut self) {
    // self.field = 1729;
    let mut deferred_assignment = || self.field = 1729;
    deferred_assignment();
    self.field = 19937;
}

And get (expected) “cannot assign to self.field because it is borrowed”. Trying the syntax || (&mut self).field = 1729; results in “closure cannot assign to immutable argument self”.

Is there any way to wrap such assignment to deferred function called later or passed to RAII-like scope guard?

Thanks!


#2

In Rust &mut is an exclusive borrow which means while the thing is exclusively borrowed, that borrow is the only way anything is allowed to interact with the thing.

In your code snippet self is exclusively borrowed by deferred_assignment and that exclusive borrow lasts until deferred_assignment goes out of scope. Because this is an exclusive borrow, Rust guarantees that no other code is allowed to interact with self through anything other than deferred_assignment. This is what disallows the last line.

You will need to make sure that deferred_assignment goes out of scope before assigning to self.field.

#[derive(Debug)]
struct Qbit86 {
    field: u32,
}

impl Qbit86 {
    fn modify_field(&mut self) {
        {
            let mut deferred_assignment = || self.field = 1729;
            deferred_assignment();
            // deferred_assignment goes out of scope here
        }
        self.field = 19937;
    }
}

fn main() {
    let mut q = Qbit86 { field: 0 };

    q.modify_field();

    println!("{:?}", q);
}

#3

You will need to make sure that deferred_assignment goes out of scope before assigning to self.field.

But in this case assignment isn’t that deferred.

One of applications is to guarantee at the beginning of some scope that field will be assigned at the end of that scope (regardless of control flow returns) in RAII manner. In C# we can use try/finally. In C++ we can use HandMadeScopeGuard capturing deferred action as std::function<void ()> and calling that closure in destructor. In Go there is defer syntax.

I was trying to do something like this:

struct ScopeGuard<F> where F: FnMut() {
    f: F,
}
...
impl<F> Drop for ScopeGuard<F> where F: FnMut() {
    fn drop(&mut self) {
        (self.f)();
    }
}

so I could write:

let deferred_assignment = || { ... };
let scope_guard_ = ScopeGuard::new(deferred_assignment);
// ...Followed by assignments and other mutating statements.

#4

I would use scopeguard which avoids exclusively borrowing the thing it needs to modify.

extern crate scopeguard;
use scopeguard::guard;

#[derive(Debug)]
struct Qbit86 {
    field: u32,
}

impl Qbit86 {
    fn modify_field(&mut self) {
        let mut q = guard(self, |q| {
            q.field = 1729;
        });

        println!("before assign = {:?}", *q);
        q.field = 19937;
        println!("after assign = {:?}", *q);
    }
}

fn main() {
    let mut q = Qbit86 { field: 0 };

    q.modify_field();

    println!("after deferred = {:?}", q);
}

#5

If I understand correctly, after capturing self in closure here, we stop using self directly, and start accessing it via deref’ing guard object. What if we still want to use self at some point later? For example, wrap closure in inner scope:

fn modify_field(&mut self) {
    {
        let mut q = guard(self, |q| {
            q.field = 1729;
        });

        println!("before assign = {:?}", *q);
        q.field = 19937;
        println!("after assign = {:?}", *q);
    }
    println!("out of scope = {:?}", self); // use of moved value: `self`
}

#6

In that case you need to avoid moving self into the guard. This gives you a guard of self with a shorter lifetime than the original &mut self:

let mut q = guard(&mut *self, |q| {
    q.field = 1729;
});