4

I need to implement the method iter, which returns something which implements the trait Iterator<Item = char>. But the return value will be different implementations, depending on the enum variant.

Something like this:

pub enum Class {
    SingleChar(char),
    Range(Range),
    And(Vec<Class>),
    Or(Vec<Class>),
}

impl Class {
    pub fn iter(&self) -> Iterator<Item = char> {
        match *self {
            Class::SingleChar(c) => vec![c],
            Class::Range(ref range) => range.iter(),
            Class::And(ref classes) => {
                let iter: Option<_> = classes.iter().fold(None, |iter, &class| {
                    match iter {
                        None => Some(class.iter()),
                        Some(iter) => Some(iter.merge(class.iter())),
                    }
                });
                Box::new(iter.unwrap())
            },
            Class::Or(ref classes) => {
                let iter: Option<_> = classes.iter().fold(None, |iter, &class| {
                    match iter {
                        None => Some(class.iter()),
                        Some(iter) => Some(iter.interleave(class.iter())),
                    }
                });
                Box::new(iter.unwrap())
            },
        }
    }
}

range.iter() returns a struct that implements Iterator<Item=char>.

merge and interleave are itertools methods, which return MergeAscend and Interleave respectively (both of them implement Iterator<Item=char>)

  1. How to implement such a scheme using static dispatch?
  2. If static dispatch is not possible, how to implement such a scheme using dynamic dispatch?

1 Answer 1

3

It is not possible to do it using static dispatch. There is a tracking RFC issue on unboxed abstract return types, but Rust is not there yet (and I'm not sure if it could cover the use case of returning different types). Therefore, dynamic dispatch is the way to go.

You're pretty close, actually. Just make the return type Box<Iterator<Item=char>> and add more boxing:

pub fn iter(&self) -> Box<Iterator<Item=char>> {
    match *self {
        Class::SingleChar(c) => Box::new(Some(c).into_iter()),
        Class::Range(ref range) => Box::new(range.iter()),
        Class::And(ref classes) => {
            let iter: Option<_> = classes.iter().fold(None, |iter, &class| {
                match iter {
                    None => Some(Box::new(class.iter())),
                    Some(iter) => Some(Box::new(iter.merge(class.iter()))),
                }
            });
            iter.unwrap()
        },
        Class::Or(ref classes) => {
            let iter: Option<_> = classes.iter().fold(None, |iter, &class| {
                match iter {
                    None => Some(Box::new(class.iter())),
                    Some(iter) => Some(Box::new(iter.interleave(class.iter()))),
                }
            });
            iter.unwrap()
        },
    }
}

This should work.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.