0
votes

This whole lifetime thing in Rust is still dark magic for me. I have a general idea of how it works but whenever I have to define lifetimes myself I have a hard time figuring out what to do. Here's what I want to do:

I have a [&str, 100] that comes from a submodule and I want to write a very simple randomized iterator that uses data from this submodule. Here's roughly what I do:

use rand::distributions::{Distribution, Uniform};
use super::data:Data;

struct RandomData {
  range: Uniform<usize>,
  rng: rand::rngs::ThreadRng,
}

impl RandomData {
  fn new () -> RandomData {
    RandomData {
      range:·Uniform::new(0, Data.len()),
      rng: rand::thread_rng(),
    }
  }
}

impl Iterator for RandomData {
  type Item = &str;

  fn next(next(&mut self) -> Option<Self::Item> {
    let index = self.range.sample(&mut self.rng);
    Some(Data[index])
  }
}

Now, obviously the compiler is asking for lifetimes here because of the &str and the easiest way would be to simply use a static lifetime &'static str. But I wondered how to do this right, so I tried the real deal.

I started with the following changes to the iterator implementation:

impl<'a> Iterator for RandomData {
  type Item = &'a str;
  fn next(next(&mut self) -> Option<Self::Item> { .. }
}

Now the compiler says: error[E0207]: the lifetime parameter 'a is not constrained by the impl trait, self type, or predicates and suggest to read more about this error E0207, which I did. I think the gist is, that the lifetime parameter needs to appear either in the Trait or implementing type. Both is not the case because I don't need it there and in this case the documentation suggests to use PhantomData. But it also only talks about types and I don't really get it to work.

If I try to do:

struct RandomData<'a> {
  range: Uniform<usize>,
  rng: rand::rngs::ThreadRng,
  phantom: PhantomData<&'a str>
}

I get a whole new bunch of messages about anonymous lifetimes, so I added them, but then get stuck with:

error[E0106]: missing lifetime specifier  --> src/epcs/random_epc.rs:12:22
   |
12 |     pub fn new () -> RandomEPC {
   |                      ^^^^^^^^^ help: consider giving it a 'static lifetime: `RandomEPC + 'static`
   |
   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from

and I'm not sure where to go from here.

Edit

Thanks phimuemue for the suggestion. I created a simplified example here: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=bddde9310da5cf838dafee83e05cd78a

1
Could you come up with an example that we could run (i.e. observe the compilation failure) in play.rust-lang.org? - phimuemue

1 Answers

0
votes

Lifetime is Rust describe how long will the data live before being drop.

In your case, the item of your Iterator is a reference to the data hold by Data. Therefore its lifetime correspond to the lifetime of Data. If Data is of lifetime static (it will live during the whole life of the process) then the right way to do your impl is to output Item with lifetime `static.

The idea with Associated Types is that the trait should be implemented only once. You can't implement twice Iterator, once with String as Item and once with &'static str. (See the book). Therefore, in your case you should implement Iterator only once with &'static str and not try to implement it for every lifetime 'l with &'l str.