The main issue with your example is that you're trying to use a trait as a type, but when you use a trait directly as a type you get an unsized type. You can't directly assign an unsized type to a variable, or return one from a function.
To get over this, you can either Box that type (as in, return a Box<Trait>
), or write your code in a generic way so that you get concrete, sized types bounded by your traits. What you're trying to do is probably the latter, but you're simply confused about how to express iterators in a generic way.
So, let's fix the example. First, the variable declaration. You wrote
let mut numbers: Iterator<Item=i32> = vec![1, 2].into_iter();
However, as I said before, Iterator<Item=i32>
is an unsized type, so you can't have variables in the stack with that type. What you want is a concrete type that implements the trait Iterator<Item=i32>
. If you really want to annotate the type, then the concrete type you want is std::vec::IntoIter<i32>
(that's what the into_iter method of a Vec returns), so this line would look like:
let mut numbers: std::vec::IntoIter<i32> = vec![1, 2].into_iter();
Of course, you don't actually need to annotate the type, so I'd recomment to just let type inference do its thing:
let mut numbers = vec![1, 2].into_iter();
Now, the function definition. You're recieving a shared (immutable) reference to an Iterator, and you want to return a new iterator that iterates over the previous one, yielding i32's? That doesn't make much sense, especially considering the body of the function:
items.map(|p| p + 1)
The map
method, as most iterator adaptors, consume the previous iterator (they take ownership of it). Therefore, you should pass the original iterator to the function, not just a reference to it (Note: this is not strictly necessary, but given what you're trying to do it is what makes the most sense). So in our function signature we want to express that we want to recieve a type (by value) that implements Iterator<Item=i32>
, and we want to return a type that also implements Iterator<Item=i32>
. The first part is done with the traditional generic syntax, and the second part with conservative_impl_trait. Taking all of this into account, the function looks like this:
fn plus_one<T>(items: T) -> impl Iterator<Item = i32>
where T: Iterator<Item = i32>
{
items.map(|p| p + 1)
}
So, we are receving a type T
that implements Iterator<Item = i32>
and we are returning another type that also implements Iterator<Item = i32>
After these changes, the example looks like this:
#![feature(conservative_impl_trait)]
fn plus_one<T>(items: T) -> impl Iterator<Item = i32>
where T: Iterator<Item = i32>
{
items.map(|p| p + 1)
}
fn main() {
let numbers = vec![1, 2].into_iter();
for i in plus_one(numbers) {
println!("{}", i);
}
println!("-");
for i in plus_one(plus_one(vec![1, 2].into_iter())) { // Yes, you can do plus_one(plus_one(iterator))
println!("{}", i);
}
}