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