Method into closure mystery

First a little background: I am targeting wasm in Rust. I have a function that uses a random number generator, and in wasm I use js-sys::Math::random for that. I want to unit test my function, so I need to use another random number generator for that. So, I make the RNG a parameter to the function as a closure. To simplify, I have

    fn return_it<RandomFunc: FnMut() -> f64 > (mut f: RandomFunc) -> f64 { f() }

Next, I set up oorandom to create random numbers:

        let mut rng = oorandom::Rand64::new(4);
        let mut randomizer = move | | oorandom::Rand64::rand_float(&mut rng);

Here is the mystery:

        let a1 = randomizer();
        let a2 = randomizer();
        assert_ne!(a1, a2);

works as expected, but

        let b1 = return_it(randomizer);
        let b2 = return_it(randomizer);
        assert_ne!(b1, b2);

fails: every time, return_it(randomizer) returns the same (random) number.

Just a thought, I haven't verified this, but:
Maybe you need to pass the closure by reference? Is randomizer getting copied on each call to return_it()?

This. Your randomizer closure, whose type is an ad-hoc anonymous compiler-generated one, seems to have auto-derived Copy.

In your case, the simplest solution is to use return_it(&mut randomizer);.

If you don't want to deal with &muts, you need the closure not to be Copy. For that, the following helper can come in handy:

fn uncopyable<'lt> (f: impl 'lt + FnMut() -> f64) // input could also be `Copy`…
  -> impl 'lt + FnMut() -> f64   // … but we don't guarantee the output is.
{
    f
}

and then do:

let mut rng = oorandom::Rand64::new(4);
let mut randomizer = uncopyable(move || oorandom::Rand64::rand_float(&mut rng));
  • (Personally, I feel like the issue here is that Rand64 : Copy when it shouldn't have, precisely because of that mutable state. It should have been Clone only, so as to make the copies explicit).

Another solution is to capture something that is zero-sized, and yet not-Copy:

let mut rng = oorandom::Rand64::new(4);
let not_copy = { struct NotCopy; NotCopy };
let mut randomizer = move || {
    let _ = not_copy; // name it in the body to capture it.
    oorandom::Rand64::rand_float(&mut rng)
};

Thanks! The &mut randomizer trick seems to work perfectly!