Having an iterator as a struct field

Hello peeps,

I have a structure that needs to periodically get an element from a cyclic pattern.
That is, my structure will be initialized with a pattern (let's say "abc"), and at various points during the struct lifetime I want to get the next element of the pattern (first time I'll get 'a', then 'b', then 'c', then 'a' again, then 'b'... ad infinitum).

My idea is therefore to create an iterator out of the cycle method and store that. Then I'll just need to call next() on it each time I want a new element! Unfortunately, by doing that I've opened the dread can of lifetime worms.

Here is an initial code that fails:

struct Foo {
    cycle: Box<dyn Iterator<Item=char>>,
}

impl Foo {
    fn new(pattern: &str) -> Self {
        Self {
            cycle: Box::new(pattern.chars().cycle()),
        }
    }
}

Predictably, this fails:

error: lifetime may not live long enough
 --> src/main.rs:9:20
  |
7 |     fn new(pattern: &str) -> Self {
  |                     - let's call the lifetime of this reference `'1`
8 |         Self {
9 |             cycle: Box::new(pattern.chars().cycle()),
  |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`

Of course I thought, iterators don't own the thing they're iterating over! So my next idea was to clone the pattern and also store it as a String next to the iterator, that way they'll have the same lifetime:

struct Foo {
    pattern: String,
    cycle: Box<dyn Iterator<Item=char>>,
}

impl Foo {
    fn new(pattern: &str) -> Self {
        let pattern = pattern.to_string();
        Self {
            pattern,
            cycle: Box::new(pattern.chars().cycle()),
        }
    }
}

But this doesn't work either:

  --> src/main.rs:12:29
   |
12 |             cycle: Box::new(pattern.chars().cycle()),
   |                    ---------^^^^^^^^^^^^^^^---------
   |                    |        |
   |                    |        borrowed value does not live long enough
   |                    cast requires that `pattern` is borrowed for `'static`
13 |         }
14 |     }
   |     - `pattern` dropped here while still borrowed

error[E0382]: borrow of moved value: `pattern`
    --> src/main.rs:12:29
     |
9    |         let pattern = pattern.to_string();
     |             ------- move occurs because `pattern` has type `String`, which does not implement the `Copy` trait
10   |         Self {
11   |             pattern,
     |             ------- value moved here
12   |             cycle: Box::new(pattern.chars().cycle()),
     |                             ^^^^^^^^^^^^^^^ value borrowed here after move
     |
     = note: borrow occurs due to deref coercion to `str`

I'm at a loss, I don't know how to express this concept. What would be a clean way to achieve that I'm trying to do? Worst comes to worst, I can always store a mutable index and play with modulos, but I'd rather take advantage of high level concepts.

Cheers,

Trait objects (like dyn Iterator) always have a lifetime parameter. The parameter has context-sensitive defaults which don't always match what you need. In the case of Box<dyn Trait> outside of a function body, that default is 'static, which precludes iterators that have temporary borrows.

Here's a version that explicitly supports a non-'static lifetime.

If the inner iterator is going to be hidden inside a private field member like this, always going to be the same, and there are no unnameable types (such as closures), you don't need the type erasure (and allocation of a Box) either.


For your second attempt, you can't trivially simultaneously own something and hold on to borrowing iterator of that same thing in a single data structure: that would be a self-referential struct, which cannot be trivially represented in Rust. That is, you need something other than the borrowing iterator Chars<'_>.

But iterators don't have to borrow what they iterate over. They can own many somethings and give them away one by one. [1] There need not be any lifetimes involved. For that, you need an iterator that owns all the data and gives it a way, but that is also cloneable (for the cycle).

One way to create that is to first collect the chars in a Vec, and then use .into_iter(). The IntoIter is an owning iterator, and because chars are Clone, it is too. (Or with type erasure.)


  1. Or that gives out copies or clones of what it owns. ↩ī¸Ž

1 Like

Thank you! I'll go with the owning version, indeed running into_iter through the intermediary vec does the trick!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.