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.

Bosh
  • 8,138
  • 11
  • 51
  • 77

1 Answers1

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.

Community
  • 1
  • 1
Francis Gagné
  • 60,274
  • 7
  • 180
  • 155
  • `!self.vals.is_some()` => `self.vals.is_none()` and some `map` on the last line would make it prettier as well. – Shepmaster Jan 22 '17 at 02: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 https://play.rust-lang.org/?gist=c43e28b29426cbc50982242f222599f8&version=nightly&backtrace=0, 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 Jan 22 '17 at 06:39
  • @Bosh: If you remove the `'a` in `&'a self` in `look_for_details`, it compiles. – Francis Gagné Jan 22 '17 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 Jan 22 '17 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). – Francis Gagné Jan 22 '17 at 22:34
  • And how should I read that? Do I think of this as "a reference with lifetime 'b to a StatefulCounter with lifetime 'a"? It seems strange to me to have an `impl` defined `for &' a StatefulCounter`that takes a `self` parameter with a different lifetime. – Bosh Jan 23 '17 at 00:35
  • In `&'b StatefulCounter<'a>`, `'a` refers to the lifetime of the `display` field and `'b` refers to the lifetime of the `StatefulCounter<'a>`. – Francis Gagné Jan 23 '17 at 01:48