Closure arguments: passing a function that mutates the inner variables

Viewed 46

The idea is to to have one closure (change_x in this case) that captures state (x in this case) that takes a function as its parameter(alterer) that would dictate how the inner state changes.

pub fn plus17(h: & u64) -> u64 {
    *h + 17
}

pub fn main() {
    let x = 0; //take x by reference

    let mut change_x = move |alterer: &dyn FnOnce(&u64) ->u64 |  alterer(&x) ;
    change_x(&mut plus17);

    println!("{}", x);
}

I can't seem to get the types right however:

error[E0161]: cannot move a value of type dyn for<'r> FnOnce(&'r u64) -> u64: the size of dyn for<'r> FnOnce(&'r u64) -> u64 cannot be statically determined
  --> playground/src/main.rs:19:69
   |
19 |     let mut increment_x = move |alterer: &dyn FnOnce(&u64) ->u64 |  alterer(&x) ;
   |                                                                     ^^^^^^^

error[E0507]: cannot move out of `*alterer` which is behind a shared reference
  --> playground/src/main.rs:19:69
   |
19 |     let mut increment_x = move |alterer: &dyn FnOnce(&u64) ->u64 |  alterer(&x) ;
   |                                                                     ^^^^^^^ move occurs because `*alterer` has type `dyn for<'r> FnOnce(&'r u64) -> u64`, which does not implement the `Copy` trait

I'm not sure if i'm justified in putting the dyn where i put it, it was a compiler's suggestion and im not really sure why i have to put it there. Is it because the alterer can be of arbitrary size despite the input/return type of &u64->u64?

I have also tried to make alterer a FnMut as opposed to FnOnce, but im also pretty shaky as to their distinction and the fact that a given alterer would run only once (at the moment of invocation by outer closure change_x) seemed reasonable.

1 Answers

FnOnce needs an owned self. Thus alterer cannot be FnOnce, because it is not owned but a reference.

You can either make it &dyn Fn or &mut dyn FnMut (I'd recommend going with FnMut), or take Box<dyn FnOnce>.

Related