Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use the Iterator trait to build generic APIs

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)

http://is.gd/EYIK11

Unfortunately, this doesn't seem to do the trick either. What am I missing?

like image 748
Christoph Avatar asked Aug 22 '15 20:08

Christoph


2 Answers

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 Iterators 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());
like image 64
Veedrac Avatar answered Sep 22 '22 08:09

Veedrac


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.

like image 20
Adrian Avatar answered Sep 19 '22 08:09

Adrian