Specifying type of recursive FlatMap

⚓ rust    📅 2025-05-21    👤 surdeus    👁️ 4      

surdeus

Warning

This post was published 39 days ago. The information described in this article may have changed.

I am trying to create an enum to return from a function.
My input is a wrapped Vec (there are other properties, but they are irrelevant).
Types are as follows (simplified, I am actually using ecow::EcoStrings instead of Strings):

enum Part {
    Static(String),
    Field(FieldDefinition),
    OptionalGroup(String, Vec<Part>)
}
struct FieldDefinition {
    name: String,
    pattern: String,
    // ...other irrelevant fields
}

I am trying to create an implementation to convert my Vec<Part> into an Iterator<Item = String> with the following logic:

impl Part {
    fn pattern_segments(&self) -> PatternSegmentIterator<'_> {
        match self {
            Part::Static(s) => PatternSegmentIter::Single(regex::escape(s).to_owned()),
            Part::Field(def) => {
                PatternSegmentIter::Single(format!("(?P<{}>{})", def.name, def.pattern))
            }
            Part::OptionalGroup(key, subparts) => PatternSegmentIter::Prefixed {
                prefix: match key {
                    Some(k) => format!("(?P<{k}>").into(),
                    None => "(?:".into(),
                },
                subparts,
                suffix: ")?".into(),
            },
        }
    }
}

My issue is with the type of PatternSegmentIter:

enum PatternSegmentIter<'a> {
    Empty,
    Single(String),
    Prefixed {
        prefix: String,
        subparts: &'a Vec<Part>,
        suffix: String,
    },
    Subparts {
        iter: std::iter::FlatMap<
            std::slice::Iter<'a, Part>,
            PatternSegmentIter<'a>,
            fn(&'a Part) -> Self,
        >,
        suffix: String,
    },
}
impl<'a> Iterator for PatternSegmentIter<'a> {
    type Item = String;

    fn next(&mut self) -> Option<Self::Item> {
        match core::mem::replace(self, Self::Empty) {
            PatternSegmentIter::Empty => None,
            PatternSegmentIter::Single(s) => Some(s),
            PatternSegmentIter::Prefixed {
                prefix,
                subparts,
                suffix,
            } => {
                *self = Self::Subparts {
                    iter: subparts.iter().flat_map(Part::pattern_segments),
                    suffix,
                };
                Some(prefix)
            }
            PatternSegmentIter::Subparts { mut iter, suffix } => match iter.next() {
                Some(item) => {
                    *self = Self::Subparts { iter, suffix };
                    Some(item)
                }
                None => Some(suffix),
            },
        }
    }
}

My issue is that the PatternSegmentIter Subparts iterator type is cyclical.
I tried to add the iterator type as a type parameter (I: Iterator<Item = String>) my build hung when attempting to specify the return type of pattern_segments as impl Iterator<Item = String>, and requiring the Iterator type to be specified is impossible (PatternSegmentIterator<'_, PatternSegmentIterator<'_, ...>>).

Is there any solution to this or do I need to switch to dynamic dispatch (return Box<dyn Iterator<Item = String> and get rid of PatternSegmentIterator)? I assumed (perhaps naiively) that creating an iterator enum would be better than just returning a boxed iterator, but maybe I'm wrong about that?

1 post - 1 participant

Read full topic

🏷️ rust_feed