Random WeightedChoice lifetime troubles


#1

I am trying to make a simple struct, which contains WeightedChoice from rand crate, but I am having lifetime troubles:

pub struct UnigramNegativeSampler<'a> {
    distribution: WeightedChoice<'a, usize>,
    choices: Vec<Weighted<usize>>
}

impl<'a> UnigramNegativeSampler<'a> {
    pub fn new(counts: &[usize]) -> UnigramNegativeSampler {
        let mut choices = counts.iter().enumerate().map(|(index, count)| {
            Weighted { weight: *count as u32, item: index }
        }).collect::<Vec<_>>();


        UnigramNegativeSampler { distribution: WeightedChoice::new(&mut choices), choices }
    }
}

However I am getting lifetime error:

error[E0597]: `choices` does not live long enough
  --> src/lib.rs:83:73
   |
83 |         UnigramNegativeSampler { distribution: WeightedChoice::new(&mut choices), choices }
   |                                                                         ^^^^^^^ borrowed value does not live long enough
84 |     }

Is there any way around this? Or am I hitting a wall with self referential structs.


#2

I think the core of the problem is that choices is an owned Vec<_> which is dropped at the end of UnigramNegativeSampler::new(). Therefore, returning something that has a any kind of borrow to it is a compile error.

I don’t know your use case, but the solution coming up in my mind is to make either of the UnigramNegativeSampler fields own the data, and then add a method to UnigramNegativeSampler to allow mutable borrowing to that data.


#3

It’s not dropped but moved into UnigramNegativeSampler. This, however, is a self referential struct, in essence.

@usamec, how expensive is it to create a new WeightedChoice from the choices on-demand?


#4

On demand creation is quite slow. But I saw some OwnedWeightedChoice code around on the internet, so it can be usable (but it is not in the rand crate, which is annoying).