I’m trying to create a custom iterator over a string’s characters, with the difference that it "splits" the string into two iterators. Which one is then used in its own next() depends on custom logic.
struct WrappingIter<I> {
iter_1: I,
iter_2: I,
// ...
}
impl<I> WrappingIter<I>
where
I: Iterator,
{
pub fn new(string: &str, start_idx: usize) -> Self {
Self {
iter_1: string[start_idx..].chars(),
iter_2: string[..start_idx].chars(),
// ...
}
}
}
That gives me this error (for both assignments):
1 error[E0308]: mismatched types
--> src/lib.rs:38:25
|
29 | impl<I> WrappingIter<I>
| - this type parameter
...
38 | iter_1: string[start_idx..].chars(),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `I`, found struct `Chars`
|
= note: expected type parameter `I`
found struct `Chars<'_>`
I can’t tell why. std::str::Chars implements the Iterator trait, so my logic was that I could directly assign it to WrappingIter‘s members. Need I perhaps perform some sort of cast?
>Solution :
This line of code can be roughly translated to "For any type I that implements the Iterator trait, define these methods in the following impl block for WrappingIter<I>" in English.
impl<I> WrappingIter<I> where I: Iterator { /* ... */ }
Notice that the type I can be any type that implements Iterator, but in the new method, an iterator of type std::str::Chars (which is not always equal to the type I) is assigned to it, which is why the code snippet fails to compile.
Here’s a solution to make it compile by manually implementing the trait separately for each iterator type. For std::str::Chars, you have to annotate the lifetime explicitly, since the iterator is borrowed from a slice on the argument string. For an alternative solution without generics (and may be more practical), see @Finomnis’s answer.
struct WrappingIter<I> {
iter_1: I,
iter_2: I,
}
impl<'i> WrappingIter<std::str::Chars<'i>> {
pub fn new(string: &'i str, start_idx: usize) -> Self {
Self {
iter_1: string[start_idx..].chars(),
iter_2: string[start_idx..].chars(),
}
}
}
// for demo purpose, try with another Iterator type
impl WrappingIter<std::ops::Range<i64>> {
pub fn new(range: std::ops::Range<i64>) -> Self {
Self {
iter_1: range.clone(),
iter_2: range.clone(),
}
}
}