2

I'm trying to implement an iterator on my own struct. My general approach is by generating and storing an iterator the first time next is invoked, and then calling this iterator each time I need a value.

My minimal failing example looks like this, and the heart of it is:

    if !self.vals.is_some() {
        self.vals = Some(Box::new({
            self.display.chars().filter(|&i| i == self.look_for)
        }) as Box<std::iter::Iterator<Item = _>>);
    }

My code fails to compile, producing the following message:

help: consider using an explicit lifetime parameter as shown: fn next(self: &'a mut Self) -> Option<<Self>::Item>

Following the advice doesn't help (just leads to more compile errors saying that my implementation is incompatible with the Iterator trait definition.

I'd appreciate help understanding what's going wrong and how I can fix it.

0

1 Answer 1

3

The problem is that the closure you pass to filter needs to borrow self, but you can't store a reference to self in the struct itself.

In this case, we can work around it by storing a copy of the value in the closure instead. This is done in two steps:

  1. Assign self.look_for to a local variable, and use the local variable in the closure instead. This way, the closure is not tied to self.
  2. Add move to the closure. The closure will thus capture the local variable by value.

Here's the final code:

impl<'a> Iterator for StatefulCounter<'a> {
    type Item = bool;
    fn next(&mut self) -> Option<Self::Item> {
        if !self.vals.is_some() {
            let look_for = self.look_for;
            self.vals = Some(Box::new({
                self.display.chars().filter(move |&i| i == look_for)
            }));
        }

        if let &Some(v) = &self.vals.as_mut().unwrap().next() {
            Some(expensive(v))
        } else {
            None
        }
    }
}

The explicit cast on the Box is not necessary, so I removed it.

7
  • !self.vals.is_some() => self.vals.is_none() and some map on the last line would make it prettier as well.
    – Shepmaster
    Commented Jan 22, 2017 at 2:51
  • This is starting to make sense—thanks for the detailed answer! But I seem to have trouble generalizing this approach: my real scenario is more like play.rust-lang.org/…, where I'm not just grabbing a field but *calling a function on self that returns (a value derived from) the field. Is there an approach that works in this situation?
    – Bosh
    Commented Jan 22, 2017 at 6:39
  • @Bosh: If you remove the 'a in &'a self in look_for_details, it compiles. Commented Jan 22, 2017 at 15:20
  • Hmm—so it does. But now I'm more confused than ever: what does it mean for a function to take a &self with no specified lifetime and return a &'a str? I would have thought 'a in this context only made sense when there was a StatefulCounter associated with it, and here we're saying the StatefulCoutner does not have lifetime 'a.
    – Bosh
    Commented Jan 22, 2017 at 16:02
  • But it does. You were trying to make self of type &'a StatefulCounter<'a> (note 'a appears twice here, the second one comes from the impl), and that's what was causing the problem (it was forcing the compiler to try to resolve an impossible lifetime, due to the active mutable borrow). All you need is a &'b StatefulCounter<'a> (where 'b can be elided). Commented Jan 22, 2017 at 22:34

Not the answer you're looking for? Browse other questions tagged or ask your own question.