Any way to avoid dynamically dispatching this iterator?

I created an iterator that moves through a vector of Strings owned by a struct(SnippetSet) stored inside a DashMap, and returns Tuples of that contains that string and it's corresponding prefix from another DashMap.

pub fn triggers(
        &self,
        language: String,
        snippet_set: String,
    ) -> Box<dyn Iterator<Item = (Vec<u8>, String)> + '_> {
        Box::new(
            self.snippet_sets //Arc<DashMap<(String,String),SnippetSets>
                .get(&(language.clone(), snippet_set.to_string()))
                .unwrap()
                .contents//Vec<String>
                .clone()//also trying to avoid cloning
                .into_iter()
                .map(move |s| {
                    (
                        self.snippets //Arc<DashMap<(String,String),Snippet>
                            .get(&(language.clone(), s.clone()))
                            .unwrap()
                            .prefix//Vec<u8>
                            .clone(),
                        s.clone(),
                    )
                }),
        )
    }

I tried wrapping the iterator in a struct as described in the simple iterator delegation section of this post on returning rust iterators, but couldn't figure out how to handle the closure inside the map iterator.
Also, I'm wondering if wrapping the vector contents inside a Cow would work for avoiding cloning while iterating?

Any ideas how I can avoid the potential performance penalty of dynamically dispatching the iterator?

What about impl Iterator?

pub fn triggers(...) -> impl Iterator<Item = (Vec<u8>, String)> + '_ { ... }
2 Likes

oh wow that works now. Originally the struct owning both of these DashMaps was wrapped in an Arc, and they were just regular DashMaps. At the time it was producing an error due to not being able to know the size at compile time, though that could be have been due to something else(I was basically building this function by trial and error)

You don't need a Cow to avoid cloning the vector.

Here's a reworked version to just clone what gets returned out of the iterator.

    pub fn triggers(
        &self,
        language: String,
        snippet_set: String,
    ) -> impl Iterator<Item = (Vec<u8>, String)> + '_ {
        let mut key = (language, snippet_set);
        let set = self.snippet_sets.get(&key).unwrap();
        set.contents.iter().cloned().map(move |s| {
            key.1 = s;
            let snippet = self.snippets.get(&key).unwrap();
            let s = std::mem::take(&mut key.1);
            (snippet.prefix.clone(), s)
        })
    }

(I used HashMap so I could do it on the playground.)

(In classic form I noticed a bug immediately after posting, so if you happened to see my post in the first 5 minutes, use the edited version or just replace key.0 = s with key.1 = s.)

I'm getting a bug when implementing the above:

cannot return value referencing local variable `sets`
returns a value referencing data owned by the current functionrustcE0515
snippet_manager.rs(76, 9): `sets` is borrowed here
snippet_manager.rs(76, 9): use `.collect()` to allocate the iterator

then I altered it to just avoid creating the local variable sets:

which led to a similar issue. it seems that any function scoped value (inlcuding iter().cloned()) is going to cause an error

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.