Reputation: 27985
I may not see the forest for the trees but I wonder how do I actually design my methods to not work against hard collection types but against Iterators instead. Consider this method.
pub fn print_strings(strings: Vec<String>) {
for val in strings.iter() {
println!("{}", val);
}
}
Obviously this falls short if I want to use that with an HashSet
or HashMap
.
So, I tried this:
use std::collections::*;
fn main () {
let strings = vec!("Foo", "Bar");
let mut more_strings = HashMap::new();
more_strings.insert("foo", "bar");
more_strings.insert("bar", "foo");
print_strings(&strings.iter());
print_strings(&more_strings.values())
}
fn print_strings(strings: &Iterator<Item=&str>) {
for val in strings {
println!("{}", val);
}
}
Playpen (also to view lengthy compiler error)
Unfortunately, this doesn't seem to do the trick either. What am I missing?
Upvotes: 6
Views: 138
Reputation: 60127
Even better, you can do
fn print_strings<Iterable>(strings: Iterable)
where Iterable: IntoIterator,
Iterable::Item: AsRef<str>
{
for val in strings {
println!("{}", val.as_ref());
}
}
(Kudos Shepmaster for the improvement.)
This means that you can call this with &mut Iterator
s for dynamic dispatch or concrete iterator or collection types for static dispatch. Further, the iterator type can be anything that can be simply converted to &str
, which includes but is not limited to &str
, &&str
and even String
.
print_strings(&strings);
print_strings(strings.iter().map(|s| s.to_owned()));
print_strings(vec![&&&&"xyz"]);
print_strings(strings);
print_strings(more_strings.values());
Upvotes: 7
Reputation: 136
playpen
So the first thing is that you're expecting Iterator<Item=&str>
, but it's Iterator<Item=&&str>
actually.
Then, you're trying to call .iter()
, but Iterator
doesn't have this method.
You can simply remove .iter()
call and receive (and send ofc) &mut Iterator<...>
in order to get for
loop working (for
loop needs something, that implements IntoIterator
, and &mut Iterator
is that thing).
Add lifetimes and you're all set! :)
Also, I'm recommending to use static dispatch. You can see it in the example I provided.
Upvotes: 4
Reputation: 15171
When you call .iter()
on a Vec<T>
, you get an Iterator<Item=&T>
. So when you call .iter()
on a Vec<&str>
, you get an Iterator<Item=&&str>
, not a Iterator<Item=&str>
. You should look at the .cloned()
method for Iterator
, it should help solve your problem.
Also, note that in order to iterate through an iterator, you must be able to mutate it (either own the iterator or have a mutable reference to it). So just having an immutable reference to it is sorta useless. I would recommend moving the iterator value into print_strings rather than passing it by reference. If you want to use trait objects for this, you can do that by using Box
, but it might be easier to just make print_strings a generic function.
Upvotes: 4