Home > Software design >  Iterator of something like str
Iterator of something like str

Time:09-16

As I am poking at Rust, I was trying to write a Rust function that can take a look at anything iterable of strings.

My initial attempt was

fn example_1(iter: impl Iterator<Item=&str>);
fn example_2(iter: impl Iterator<Item=&String>);
// ...

that requires two functions for different cases.

I also tried combining cases above into one, and what I got is:

fn example_3<'a, T: 'a   ?Sized   Deref<str>>(iter: impl Iterator<Item=&'a T>);

that can accept both Iterator<Item=&String> and Iterator<Item=&str>, but not Iterator<String> (because last one requires one more borrow in the function body). Also, it looks way more complex than it probably should.

I of course can implement this using custom trait, maybe involving AsRef, but I think I might be missing a better and clearer way.

CodePudding user response:

You can do this more easily using two generics, one to represent the string type and one for the iterator. While you can do this with Deref, the AsRef trait would be better here since there can be multiple AsRef implementations for a given type, and it's a little bit more generic. Here's an example that will work with &str, &String, String, and so on.

fn example<S: AsRef<str>, T: Iterator<Item=S>>(iter: T) {
    for string in iter {
        println!("String: {}", string.as_ref());
    }
}

CodePudding user response:

This is an excellent use case for std::borrow::Borrow. Borrow is for types that can reasonably be borrowed into a similar-looking but not literally identical type (String borrows as &str, PathBuf borrows as &Path, etc.)

fn example<'a, I, T>(iter: I)
where I : Iterator<Item=T>,
      T : Borrow<str> {
  for x in iter {
    let my_string: &str = x.borrow();
    // ...
  }
}

Note that this will work for String and &str, but not, as you suggest in your example, &String. If you end up with an iterator of &String, then you might question how you got there (&String is useless for the same reason &Vec<T> is; it's objectively worse than &str in every way). But if you for some reason wind up with an iterator of &String, you can get to &str with

my_iter.map(|x| &**x));

One * gets rid of the &, one * derefs the String to str, then the & borrows the str. Congratulations! You're a two-star programmer now.

Or, more simply (thanks, user4815162342),

my_iter.map(|x| x.as_str())
  • Related