Lapshin Dmitry
Lapshin Dmitry

Reputation: 1124

Iterator of something like str

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.

Upvotes: 3

Views: 1011

Answers (2)

Silvio Mayolo
Silvio Mayolo

Reputation: 70267

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())

Upvotes: 7

apetranzilla
apetranzilla

Reputation: 5929

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());
    }
}

Upvotes: 7

Related Questions