Borrow checker error when passing peekable string to struct

I have a peculiar design question here (or rather basic one for experienced rustaceans here :slight_smile:).

I am trying to implement a custom iterator which would take a &str parameter and create a structure, which has iterator inside, like this:

struct TokenIterator<'a> {
    iterator: Peekable<Chars<'a>>,
}

impl<'a> TokenIterator<'a> {
    fn new(input: &'a str) -> Self {
        let lowercase = input.to_lowercase();
        TokenIterator {
            iterator: lowercase.chars().peekable(),
        }
    }
}

As expected, I get an error:

error[E0597]: `lowercase` does not live long enough
  --> src\lib.rs:37:23
   |
37 |             iterator: lowercase.chars().peekable(),
   |                       ^^^^^^^^^ borrowed value does not live long enough
38 |         }
39 |     }
   |     - borrowed value only lives until here
   |
note: borrowed value must be valid for the lifetime 'a as defined on the impl at 33:6...
  --> src\lib.rs:33:6
   |
33 | impl<'a> TokenIterator<'a> {
   |      ^^

I understand the cause for the issue, but I can’t figure out how I should rewrite the code to make it work. Maybe, redesign is required?

The point is: I want to pass a string to this custom iterator, so that it then contains an iterator over lowercase chars of given string, so that I later can enumerate over it in actual iterator implementation.

Easiest would be to encapsulate the lower cased String and expose the iter off that, e.g.:

struct LCString(String);

impl LCString {
    fn new(input: &str) -> Self {
        LCString(input.to_lowercase())
    }

    fn token_iter(&self) -> Peekable<impl Iterator<Item = char> + '_> {
        self.0.chars().peekable()
    }
}

An alternative is to go directly off the str;

struct TokenIterator<'a> {
    iterator: Peekable<Box<dyn Iterator<Item=char> + 'a>>,
}

impl<'a> TokenIterator<'a> {
    fn new(input: &'a str) -> Self {
        let i = input.chars().flat_map(|c| c.to_lowercase());
        let b = Box::new(i) as Box<dyn Iterator<Item=_>>;
        TokenIterator {
            iterator: b.peekable(),
        }
    }
}

I think I like @jonh’s solution more, as the interface is more straightforward to use, and also it is close to my original struct, even though it looks so cryptic for me on current level of Rust understanding. If I were to write the same code from scratch, I couldn’t write it :smile:. I yet have to learn how to read and understand this code.

One issue with that approach, if you’re unfamiliar, is iteration will involve virtual (dynamic) dispatch - you may or may not care.

You can also create a hybrid approach that doesn’t involve dynamic dispatch:

struct TokenIterator<I: Iterator> {
    iterator: Peekable<I>,
}

fn token_iter(input: &str) -> TokenIterator<impl Iterator<Item = char> + '_> {
    TokenIterator {
        iterator: input.chars().flat_map(char::to_lowercase).peekable(),
    }
}