Reusable iterators over string references

I have a function that needs to take in a list of keys (strings), which need to be iterated over multiple times.

This works:

fn decode_list<I, K>(it: I)
where
  I: IntoIterator<Item = K> + Clone,
  K: AsRef<str>
{
  for idx in 0..2 {
    for n in it.clone() {
      let nm = n.as_ref();
    }
  }
}

fn main() {
  decode_list(["Id", "Name", "Age"]);
}

But I'm curious if it's possible to eliminate the need for the Clone? There are a number of posts about this, using HRTB, on these forums, but my attempts at adapting them cause the call to decode_list() to no longer be accepted.

Edit: Fixed the Pennywise Freudian slip

:laughing:

no, you cannot.

but you don't have to, iterators can be cheap to clone, although you do need to find the right iterator type. in your example, cloning an array is not cheap when the array is large, but cloning a reference or slice (or an explicit Iterator like std::slice::Iter) is very cheap, i.e.

fn main() {
  // this will clone the array, which is bad for large array
  decode_list(["Id", "Name", "Age"]);
  // this will clone the array reference
  decode_list(&["Id", "Name", "Age"]);
  // this will clone the slice
  decode_list(&["Id", "Name", "Age"][..]);
  // this will clone the `std::slice::Iter`
  decode_list(["Id", "Name", "Age"].iter());
}
2 Likes

My intuition was to use references and use HRTB to say a reference to I implements IntoIterator.
That seems to have worked, but I feel there's a gotcha somewhere:

fn decode_list<I, K>(it: I)
where
    for<'i> &'i I: IntoIterator<Item = &'i K>,
    for<'k> &'k K: AsRef<str>,
{
    for idx in 0..2 {
        for n in &it {
            let nm = n.as_ref();
        }
    }
}

Playground

1 Like

It takes what is usually collection by value, but only uses it by reference.

If you take it by reference, you don't need the HRTB... and this is also just a less general version of your OP.

2 Likes