Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to most generically iterate a sequence of items with a given type?

Tags:

rust

The code below best describes the issue.

use std::iter::IntoIterator;

fn iterate<I: IntoIterator<Item=String>>(v: I) {
}

// iterate(&["foo".to_string()])
// error: type mismatch resolving `<&[collections::string::String; 1] as core::iter::IntoIterator>::Item == collections::string::String`:
// expected &-ptr,
// found struct `collections::string::String` [E0271]

// iterate(["foo".to_string()].iter())
// type mismatch resolving `<core::slice::Iter<'_, collections::string::String> as core::iter::IntoIterator>::Item == collections::string::String`:
// expected &-ptr,
// found struct `collections::string::String` [E0271]

// This works !
iterate(vec!["foo".to_string()])

How can I iterate anything (with a given item type) generically?

Additional Notes

The intention is to allow users of such a function to pass in anything that can be iterated, or converted into an iterator.

Also I have the feeling that the actual issue is not really described in the compiler error - as the type it sees seems to be different from what it shows.

I am using rustc 1.0.0-nightly (522d09dfe 2015-02-19) (built 2015-02-19)

like image 746
Byron Avatar asked Feb 21 '15 18:02

Byron


1 Answers

Let's look at what the types are for your first case:

for i in &["foo".to_string()] {
    let () = i;
    // expected `&collections::string::String`,
    // found `()`
}

That is, the type of your iteration variable is &String, not on String, as your function wants. The same thing happens for your second case. The third case works:

for i in vec!["foo".to_string()] {
    let () = i;
    // expected `collections::string::String`,
    // found `()`
}

We can look at the implementation for IntoIter for arrays and for all 3 forms of Vec. Note that the implementation for Vec<T> consumes the vector, whereas the one for &Vec<T> must return an iterator over references to the elements.

You can't consume an array, so that iterator must always return references.

Here's an example that uses the AsRef trait that should do what you want:

use std::iter::IntoIterator;

fn iterate<I, S>(v: I)
    where I: IntoIterator<Item=S>,
          S: AsRef<str>
{}

fn main() {
    iterate(&["foo"]);
    iterate(&["foo".to_string()]);
    iterate(["foo".to_string()].iter());
    iterate(vec!["foo".to_string()]);
    iterate(&vec!["foo".to_string()]);
}

This says that we expect any concrete type that implements the trait Iterator. That iterator must yield a type that implements the trait AsRef<str>, allowing us to pass in {arrays,vectors,slices} of {String,&str}.

like image 115
Shepmaster Avatar answered Oct 20 '22 06:10

Shepmaster