Alex F
Alex F

Reputation: 259

How to use a FnMut argument in a closure without moving it or requiring Copy?

I'm still a little confused on how the Fn-FnMut-FnOnce thing works with other traits. I want to eliminate the Copy constraint from the following function for traversing a tree structure.

pub fn for_each<F>(&mut self, mut f: F)
where
    F: FnMut(&mut Tree<T>) + Copy,
{
    self.children.iter_mut().for_each(|c| c.for_each(f));
    f(self);
}

The reason for this is that I am trying to pass a closure that modifies an outside variable to for_each and the Copy prevents that (E0277). However, when I get rid of the Copy, I get the following error message:

error[E0507]: cannot move out of `f`, a captured variable in an `FnMut` closure
  --> src/tree.rs:34:58
   |
30 |     pub fn for_each<F>(&mut self, mut f: F)
   |                                   ----- captured outer variable
...
34 |         self.children.iter_mut().for_each(|c| c.for_each(f));
   |                                                          ^ move occurs because `f` has type `F`, which does not implement the `Copy` trait

error[E0382]: borrow of moved value: `f`
  --> src/tree.rs:35:9
   |
30 |     pub fn for_each<F>(&mut self, mut f: F)
   |                                   ----- move occurs because `f` has type `F`, which does not implement the `Copy` trait
...
34 |         self.children.iter_mut().for_each(|c| c.for_each(f));
   |                                           ---            - variable moved due to use in closure
   |                                           |
   |                                           value moved into closure here
35 |         f(self);
   |         ^ value borrowed here after move
   |
help: consider further restricting this bound
   |
32 |         F: FnMut(&mut Tree<T>) + Copy,
   |                                ^^^^^^

error: aborting due to 2 previous errors

How can I solve this issue? I'm also open to turning this into an iterator if that makes it easier.

Upvotes: 3

Views: 1610

Answers (1)

LordMZTE
LordMZTE

Reputation: 227

The reason for this is, that for_each takes ownership of f.

This means, that once you call c.for_each(f), you loose ownership of f, thus you cannot use f later. In order to fix this, you could change the for_each function to instead take a reference, so you can pass it along like this

pub fn for_each<F>(&mut self, f: &mut F)
where
    F: FnMut(&mut Tree<T>),
{
    // Only a reference is passed into for_each here
    self.children.iter_mut().for_each(|c| c.for_each(f));
    // f can be used again, as reference is reborrowed implicitly
    f(self);
}

Or you could also (if possible) move the call to f up a line like this

pub fn for_each<F>(&mut self, mut f: F)
where
    F: FnMut(&mut Tree<T>),
{
    // f is used, but ownership is kept
    f(self);
    // ownership of f can now be transferred, as there is no further use of it
    self.children.iter_mut().for_each(|c| c.for_each(f));
}

Upvotes: 4

Related Questions