1
votes

I seem to be having issues, with Rust, and would certainly appreciate some help.

I've a custom struct that implements the Iterator trait, and a separate struct (also an Iterator) that I'd like to be able to wrap anything that presents itself as an iterable. Some of this seems achievable via generics, however, when getting specific within the wrapper struct things seem to go sideways.

My main goal is to accept any iterable that returns Option<char>, and preform some parsing/collection of characters.

Here's a toy example that demonstrates the core features I'm trying to implement...

src/main.rs

#!/usr/bin/env rust


struct IteratorHolder<I, T>
where
    I: Iterator<Item = T>
{
    iter: I,
}

impl<I, T> Iterator for IteratorHolder<I, T>
where
    I: Iterator<Item = T>
{
    type Item = T;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next()
    }
}

impl<I, T> IteratorHolder<I, T>
where
    I: Iterator<Item = T>
{
    #[allow(dead_code)]
    fn new(iter: I) -> Self {
        Self { iter }
    }
}

impl<I, T> From<I> for IteratorHolder<I, T>
where
    I: Iterator<Item = T>,
{
    fn from(iter: I) -> Self {
        Self { iter }
    }
}


// ============================================================================


struct CustomIterator {
    data: Option<(usize, String)>
}

impl CustomIterator {
    #[allow(dead_code)]
    fn new<S>(string: S) -> Self
    where
        S: Into<String>
    {
        let string: String = string.into();
        let data = Some((0, string));
        Self { data }
    }
}

impl Iterator for CustomIterator {
    type Item = char;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some((index, string)) = self.data.take() {
            let mut iter = string.get(index..).unwrap().char_indices();
            if let Some((_, c)) = iter.next() {
                self.data = iter.next().map(|(i, _)| (index + i, string));
                return Some(c);
            }
        }
        None
    }
}


// ============================================================================


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn map() {
        let v = vec![1, 2, 3];
        let m = v.into_iter().map(|n| n * 2);
        let mut i = IteratorHolder::new(m);
        assert_eq!(i.next(), Some(2));
    }

    #[test]
    fn chars() {
        let s = String::from("abc");
        let c = s.chars();
        let mut i = IteratorHolder::new(c);
        assert_eq!(i.next(), Some('a'));
    }

    #[test]
    fn custom_iterator() {
        let c = CustomIterator::new("zxc");
        let mut i = IteratorHolder::new(c);
        assert_eq!(i.next(), Some('z'));
    }

    #[test]
    fn from_iter() {
        let c = CustomIterator::new("zxc");
        let mut i = IteratorHolder::from(c);
        assert_eq!(i.next(), Some('z'));
    }
}

Above all seems to function without errors


What I'm finding strange is when getting specific with From implementations there be various type errors appearing.

For example where I to implement FromStr on IteratorHolder a E0053 error is generated...

method from_str has an incompatible type for trait expected fn pointer fn(&str) -> std::result::Result<IteratorHolder<I, T>, _> found fn pointer fn(&str) -> std::result::Result<IteratorHolder<CustomIterator, char>, _> [E0053]

src/main.rs (snip)

use std::str::FromStr;
use std::num::ParseIntError;


impl<I, T> FromStr for IteratorHolder<I, T>
where
    I: Iterator<Item = T>,
{
    type Err = ParseIntError;

    fn from_str(s: &str) -> Result<IteratorHolder<CustomIterator, char>, Self::Err> {
        let iter = CustomIterator::new(s);
        let hold = IteratorHolder { iter };
        Ok(hold)
    }
}

... And being more generic generates E0308 errors...

mismatched types expected type parameter I found struct CustomIterator [E0308]

impl<I, T> FromStr for IteratorHolder<I, T>
where
    I: Iterator<Item = T>,
{
    type Err = ParseIntError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let iter = CustomIterator::new(s);
        let hold = Self { iter };
        Ok(hold)
    }
}

Essentially I'm face-planting on how to make IteratorHolder more self sufficient, with regards to From implementations. My main goal is to have a parser, similar to IteratorHolder, that can read characters from iterators regardless of where those characters are sourced from; eg. file, stream, string. etc.

1
When you say impl<I, T> FromStr for IteratorHolder<I, T> where I: Iterator<Item = T> you're saying "for every IteratorHolder I will implement FromStr." Yet, your implementation returns only an IteratorHolder for CustomIterator, which is clearly not what your type declaration said.Aplet123

1 Answers

1
votes

As @Aplet123 mentioned, you are claiming to implement FromStr for an IteratorHolder containing any iterator, however you really just want to implement it for IteratorHolder<CustomIterator>. Here's a working version, cleaned up a bit and more idiomatic (particularly: I removed T since it is redundant, it already exists as I::Item):

Playground

use std::num::ParseIntError;
use std::str::FromStr;

struct IteratorHolder<I> {
    iter: I,
}

impl<I: Iterator> Iterator for IteratorHolder<I> {
    type Item = I::Item;

    fn next(&mut self) -> Option<Self::Item> {
        self.iter.next()
    }
}

impl<I: Iterator> IteratorHolder<I> {
    #[allow(dead_code)]
    fn new(iter: I) -> Self {
        Self { iter }
    }
}

impl<I: Iterator> From<I> for IteratorHolder<I> {
    fn from(iter: I) -> Self {
        Self::new(iter)
    }
}

// ============================================================================

struct CustomIterator {
    data: Option<(usize, String)>,
}

impl CustomIterator {
    #[allow(dead_code)]
    fn new<S: Into<String>>(string: S) -> Self {
        Self {
            data: Some((0, string.into())),
        }
    }
}

impl Iterator for CustomIterator {
    type Item = char;

    fn next(&mut self) -> Option<Self::Item> {
        if let Some((index, string)) = self.data.take() {
            let mut iter = string.get(index..).unwrap().char_indices();
            if let Some((_, c)) = iter.next() {
                self.data = iter.next().map(|(i, _)| (index + i, string));
                return Some(c);
            }
        }
        None
    }
}

impl FromStr for IteratorHolder<CustomIterator> {
    type Err = ParseIntError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        Ok(IteratorHolder {
            iter: CustomIterator::new(s),
        })
    }
}