Pass a Closure that takes a mutable reference to self

I am trying to figure out how I can call a closure which is a member of structure from one of the structures methods.

An simplified example is is below, ideally I want to be able to pass a closure to. struct which manipulates some of the struct's data members when a generate() function is called. This will allow the calling code to change the behaviour of the internal algorithim:

struct Foo {
    message: String,
    val: u32,
    operation: Box<dyn Fn(&mut Foo)>,
}

impl Foo {
    fn do_stuff(&mut self) {
        (self.operation)(&mut self);
        println!("message={}, val={}", self.message, self.val);
    }
}

fn main() {
    println!("Hello, world!");

    let mut my_foo = Foo {
        message: "original message".to_string(),
        val: 0,
        operation: Box::new(|x: &mut Foo|{
            x.message = "New Message".to_string().to_owned();
            x.val += 1000;
        }),
    };

    my_foo.do_stuff();
}

The problem is:

error[E0596]: cannot borrow `self` as mutable, as it is not declared as mutable
  --> src/main.rs:10:26
   |
10 |         (self.operation)(&mut self);
   |                          ^^^^^^^^^ cannot borrow as mutable
   |
note: the binding is already a mutable borrow
  --> src/main.rs:9:17
   |
9  |     fn do_stuff(&mut self) {
   |                 ^^^^^^^^^
help: try removing `&mut` here
   |
10 -         (self.operation)(&mut self);
10 +         (self.operation)(self);
   |

error[E0502]: cannot borrow `self` as mutable because it is also borrowed as immutable
  --> src/main.rs:10:26
   |
10 |         (self.operation)(&mut self);
   |         ---------------- ^^^^^^^^^ mutable borrow occurs here
   |         |
   |         immutable borrow occurs here
   |         immutable borrow later used by call

Some errors have detailed explanations: E0502, E0596.
For more information about an error, try `rustc --explain E0502`.
error: could not compile `recursive_closure` due to 2 previous errors

The problem line is (self.operation)(&mut self);.

I am new to rust and this is a pattern I have previously used with C++ in the past.

fn do_stuff(&mut self) is shorthand for fn do_stuff(self: &mut Self). Because self already has the type &mut Self, the statement (self.operation)(&mut self) is re-borrowing the mutable reference. However, the variable self isn't mut self. This is what the compiler error is indicating -- self can't be borrowed mutably because it isn't declared as such. The compiler suggestions are sadly not very good in this situation.

The easier solution is to use (self.operation)(self) because self is already the correct type needed for the function call.

I believe the compiler will begin complaining that self can't be borrowed mutably multiple times -- that isn't as easy of a fix, and generally means restructuring your types to avoid the need for multiple mutable borrows of the same data.

Ok son changing the call in do_stuff(&mut self) to (self.operation)(self); results in the following:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
  --> src/main.rs:10:9
   |
10 |         (self.operation)(self);
   |         ----------------^^^^^^
   |         |
   |         mutable borrow occurs here
   |         immutable borrow occurs here
   |         immutable borrow later used by call

For more information about this error, try `rustc --explain E0502`.
error: could not compile `recursive_closure` due to previous error

In my original use case I restructured the code but I am not happy with the solution as I would like to store a closure that operates on some_type inside the type so that its internal algorithm(s) can use the closure.

A c++ version of Foo would look like:

#include <string>
#include <iostream>
#include <cstdint>
#include <functional>

struct Foo {
    using Closure = std::function<void(Foo&)>;

    uint32_t val_;
    std::string message_;
    Closure operation_;

    Foo(uint32_t v, std::string m, Closure op)
        : val_{v}
        , message_{std::move(m)}
        , operation_{std::move(op)} {
    }

    void do_stuff() {
        operation_(*this);
        std::cout << "message=" << message_ << ", val=" << val_ << "\n";
    }
};

int main() {
    Foo foo{0, "Original Message", [](Foo& foo) {
        foo.message_ = "New Message";
        foo.val_ += 1000;
    }};

    foo.do_stuff();
}

The main issue is that the function call borrows self.operation immutably, but it gives the closure mutable access to self.operation through the &mut Foo parameter. While the most idiomatic solution is to extract message and val into their own type and pass that to operation, I see two other solutions with the current Foo type. The first is to take ownership of operation by replacing it with a no-op placeholder:

use std::mem;

struct Foo {
    message: String,
    val: u32,
    operation: Box<dyn Fn(&mut Foo)>,
}

impl Foo {
    fn do_stuff(&mut self) {
        let operation = mem::replace(&mut self.operation, Box::new(|_| {}));
        operation(self);
        self.operation = operation;
        println!("message={}, val={}", self.message, self.val);
    }
}

But note that do_stuff() cannot be called recursively this way, and if operation() panics, then self will be left with the placeholder. (The latter issue can be fixed with a drop guard.) Perhaps a more robust solution is to place operation in an Rc, to allow shared ownership:

use std::rc::Rc;

struct Foo {
    message: String,
    val: u32,
    operation: Rc<dyn Fn(&mut Foo)>,
}

impl Foo {
    fn do_stuff(&mut self) {
        let operation = self.operation.clone();
        operation(self);
        println!("message={}, val={}", self.message, self.val);
    }
}

fn main() {
    println!("Hello, world!");

    let mut my_foo = Foo {
        message: "original message".to_string(),
        val: 0,
        operation: Rc::new(|x: &mut Foo| {
            x.message = "New Message".to_string();
            x.val += 1000;
        }),
    };

    my_foo.do_stuff();
}

This has none of the caveats of the former solution. Note that since operation is behind an Rc, it can't always be moved out of the Foo, but this is unlikely to be a major issue in practice.

1 Like

@LegionMammal978 Thanks.

Extracting the variables into their own type is the solution I ended up with in the full use case but seeing the closure with the data is my preferred solution. Your explanation certainly clears up my understanding of what is happening and the second solution provided is nice a neat (effectively changing the storage of operation to an Rc and cloning operation prior to the call).

Alternative 1

struct Inner {
    message: String,
    val: u32,
}

struct Foo {
    inner: Inner,
    operation: Box<dyn Fn(&mut Inner)>,
}

impl Foo {
    fn do_stuff(&mut self) {
        (self.operation)(&mut self.inner);
        let inner = &self.inner;
        println!("message={}, val={}", inner.message, inner.val);
    }
}

fn main() {
    println!("Hello, world!");

    let mut my_foo = Foo {
        inner: Inner {
            message: "original message".to_string(),
            val: 0,
        },
        operation: Box::new(|x: &mut Inner| {
            x.message = "New Message".to_string();
            x.val += 1000;
        }),
    };

    my_foo.do_stuff();
}

Alternative 2

use std::borrow::Cow;

struct Foo<'a> {
    message: Cow<'a, str>,
    val: u32,
    operation: &'a dyn Fn(&mut Foo),
}

impl<'a> Foo<'a> {
    fn do_stuff(&mut self) {
        (self.operation)(self);
        println!("message={}, val={}", self.message, self.val);
    }
}

fn main() {
    println!("Hello, world!");

    let mut my_foo = Foo {
        message: Cow::from("original message"),
        val: 0,
        operation: &|x: &mut Foo| {
            x.message = Cow::from("New Message");
            x.val += 1000;
        },
    };

    my_foo.do_stuff();
}

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.