Borrow closure member inside struct bounded method

I did some research and discovered that Rust tries to borrow the closure struct member before I call it with (). Can anyone explain why Rust is trying to borrow self.closure? What is the logic behind this? This confused me quite a lot.

Moreover the type of reference is related to what kind of Fn... trait the closure implements. In this particular case, if I change FnMut to Fn, no errors occur, because we can have multiple shared references to self (including self.closure), but we cannot create unique reference to self.closure through a regular reference to it.

pub struct S<F> {
    closure: F,
}

impl<F: FnMut()> S<F> {
    pub fn test(&self) {
        (self.closure)(); // <-- error
    }
}

fn main() {
    let s = S { closure: || () };
    s.test();
    let s_ref = &s;
    (s_ref.closure)(); // <-- no error
}

(Playground)

Errors:

(self.closure)();
^^^^^^^^^^^^^^ `self` is a `&` reference, so the data it refers to cannot be borrowed as mutable
help: consider changing this to be a mutable reference

pub fn test(&mut self) {
            ~~~~~~~~~

It's an FnMut, which means that you want it to be callable by (mutable) reference.

When you create a closure, it's like defining a new (anonymous) struct that contains any captures the closures may have as fields. The compiler also automatically implements various traits for that struct, most pertinently, one or more of the Fn traits.

As you can see from the signature in the trait, calling a FnMut requires a &mut self to the implementor -- to the closure. Why? Because the closure is allowed to mutate its captures, and just like you need a &mut self to mutate your[1] fields, a closure needs a &mut self to mutate its[2] captures.

If you don't want to require &mut, use a Fn (or FnOnce, when applicable) bound instead. (That's what's going on with the version in main that compiles.)


  1. non-interior-mutability ↩︎

  2. non-interior-mutability ↩︎

2 Likes

This is a great explanation. Thanks a lot! Could you elaborate on why this works in the main?

Here is a straightforward analogy that does not compile with an error: cannot borrow *x_ref as mutable, as it is behind a & reference. Please point me where I am going wrong here.

fn main() {
    let mut x = 0;
    let x_ref = &x;
    let x_ref_mut = &mut *x_ref; // <-- error
}

Does this make it any more clear? The "closure" (i32) implements MyFn and MyFnMut. But in test, you can only utilize MyFnMut, because that's the bound you mentioned on the generic F. In main, the concrete type is known, and all of it's trait implementations -- including MyFn -- can be utilized.

I'm not sure how your assignment example relates, so I can't comment on that.

Yes. Great! You have successfully unraveled what exactly I understand wrong, despite my inappropriate example. I've corrected it to demonstrate what I mean, but no further explanation is needed.
In a word: I didn't take into account that the bound only applies to the closure in the the test method, but not to the closure in general.

1 Like

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.