3

I have a PRNG that I would like to allow a closure to access by mutable reference. The lifetimes of everything should theoretically be able to work out, here is what it looks like:

fn someFunction<F, I>(mut crossover_point_iter_generator: F)
        where F: FnMut(usize) -> I, I: Iterator<Item=usize>;

let mut rng = Isaac64Rng::from_seed(&[1, 2, 3, 4]);
someFunction(|x| (0..3).map(move |_| rng.gen::<usize>() % x));

Here, a closure is creating an iterator that wraps PRNG generated values. This iterator contains a map with a closure that has the wrap range x cloned into it, but the problem is that it unintentionally clones rng as well, which I have verified. It is necessary to make it a move closure because the value of x must be captured, otherwise the closure will outlive x.

I attempted to add this line to force it to move the reference into the closure:

let rng = &mut rng;

However, Rust complains with this error:

error: cannot move out of captured outer variable in an `FnMut` closure

Can I mutably access the PRNG from inside the move closure, and if not, since the PRNG clearly outlives the function call, is there an alternative solution (aside from redesigning the API)?

Edit:

I have rewritten it to remove the copy issue and the call looks like this:

someFunction(|x| rng.gen_iter::<usize>().map(move |y| y % x).take(3));

This results in a new error:

error: cannot infer an appropriate lifetime for autoref due to conflicting requirements
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
vadix
  • 105
  • 1
  • 6
  • 1
    Incidental, but [**do not use** modulo to get random numbers in a range](http://stackoverflow.com/questions/10984974/why-do-people-say-there-is-modulo-bias-when-using-a-random-number-generator)! The `rand` crate has a "random within a range" function for this case. – Shepmaster Nov 02 '15 at 19:47
  • 1
    @Shepmaster Ahh, thank you. I am familiar with the bias issue, but I didn't realize Rng exposed an interface to take care of this easily! It would probably be best to look over all the functions the traits I am using expose. I am going to assume it was [rand::Rng::gen_range()](https://doc.rust-lang.org/num/rand/trait.Rng.html#method.gen_range)? – vadix Nov 03 '15 at 03:11
  • Yeah, I was too lazy to find the function ^_^. There's also the [long version](https://doc.rust-lang.org/num/rand/distributions/range/struct.Range.html), but `gen_range` is much more understandable. – Shepmaster Nov 03 '15 at 03:14
  • @Shepmaster Does the standard library include any mechanism to get an iterator similarly to gen_iter for generating values in a range? I really like Rust iterators, and I am trying to find ways to use them efficiently to solve problems. I noticed the [SampleRange](https://doc.rust-lang.org/num/rand/distributions/range/trait.SampleRange.html) trait, and I also noticed the [Generator](https://doc.rust-lang.org/num/rand/struct.Generator.html) iterator, but there seems to be no way to get a generator that calls gen_range() on Rng instead of gen(). – vadix Nov 03 '15 at 05:58
  • 1
    I'd suggest asking a new question here on SO for that; that makes it easier for other people to find the same thing in the future! ^_^ – Shepmaster Nov 03 '15 at 12:39

1 Answers1

6

You're got a situation that requires multiple conflicting mutable borrows, and rustc is denying this as it should. It's just up to us to understand how & why this happens!

A note that will be important:

  • Isaac64Rng implements Copy, which means that it implicitly copies instead of just moving. I'm assuming is is a legacy / backwards compatibility thing.

I wrote this version of the code to get it straight:

extern crate rand;

use rand::Isaac64Rng;
use rand::{Rng, SeedableRng};

fn someFunction<F, I>(crossover_point_iter_generator: F)
    where F: FnMut(usize) -> I, I: Iterator<Item=usize>
{
    panic!()
}

fn main() {
    let mut rng = Isaac64Rng::from_seed(&[1, 2, 3, 4]);
    let rng = &mut rng;  /* (##) Rust does not allow. */
    someFunction(|x| {
        (0..3).map(move |_| rng.gen::<usize>() % x)
    });
}

Let me put this in points:

  • someFunction wants a closure it can call, that returns an iterator each time it's called. The closure is mutable and can be called many times (FnMut).

  • We must assume all the returned iterators can be used at the same time, and not in sequence (one at a time).

  • We would like to borrow the Rng into the iterator, but mutable borrows are exclusive. So borrowing rules do not allow more than one iterator at a time.

  • FnOnce instead of FnMut would be one example of a closure protocol to help us here. It would make rustc see that there can be only one iterator.

In the working version, without the line (##), you have several iterators active at the same time, what's happening there? It's the implicit copying kicking in, so each iterator will use an identical copy of the original Rng (sounds undesirable).

Your second version of the code runs into essentially the same limitation.

If you want to work around the exclusivity of borrowing, you can use special containers like RefCell or Mutex to serialize access to the Rng.

bluss
  • 12,472
  • 1
  • 49
  • 48
  • 3
    *assume all the returned iterators can be used at the same time* — +1. This is IMHO the key thing to learn to help debug this kind of thing in the future. – Shepmaster Nov 02 '15 at 20:21
  • @Shepmaster Thank you for emphasizing that. This is where my misunderstanding was. Luckily it was satisfactory to change this to a FnOnce. – vadix Nov 03 '15 at 03:04