Reputation: 559
I am defining a trait that takes in a i: &I
parameter. I would like to use this i
value in a for
loop.
For example:
struct Test;
trait Bar<I> {
fn bar(&self, i: &I);
}
impl<T, I: IntoIterator<Item=T>> Bar<I> for Test {
fn bar(&self, i: &I) {
for x in i {
println!("woo!");
}
}
}
fn main() {
let vec = vec!(1, 2, 3);
let test = Test;
test.bar(&vec);
}
This results in the error:
<anon>:10:9: 12:10 error: the trait `core::iter::Iterator` is not implemented for the type `&I` [E0277] <anon>:10 for x in i { <anon>:11 println!("woo!"); <anon>:12 } <anon>:10:9: 12:10 help: see the detailed explanation for E0277 <anon>:10:9: 12:10 note: `&I` is not an iterator; maybe try calling `.iter()` or a similar method <anon>:10:9: 12:10 note: required by `core::iter::IntoIterator::into_iter` error: aborting due to previous error playpen: application terminated with error code 101
I was playing around with using the Deref
trait to see if I could get something to work, but to no avail.
I would really like to keep the immutable reference in the function definition as this trait is attempting to be generic over many types, and defining the other implementations using Bar<&'a I>
has lead to some other lifetime related issues that I have been having trouble with as well.
Upvotes: 5
Views: 1831
Reputation: 65782
In Rust, by convention, methods whose name starts with into
take their argument by value and transform it into another value, usually reusing some resources from the original value. The IntoIterator
trait and its into_iter
follow that convention.
Are you sure you need to use &I
in your trait? Your codes works just fine with I
. That's because there is an implementation of IntoIterator
for &Vec<T>
. That's why one can write for x in &v
where v
is a Vec
.
struct Test;
trait Bar<I> {
fn bar(&self, i: I);
}
impl<T, I: IntoIterator<Item=T>> Bar<I> for Test {
fn bar(&self, i: I) {
for x in i {
println!("woo!");
}
}
}
fn main() {
let vec = vec!(1, 2, 3);
let test = Test;
test.bar(&vec);
}
Upvotes: 2
Reputation: 102096
Saying that I
is an IntoIterator
doesn't say anything about &I
, e.g. x..y
is an IntoIterator
(because it is an Iterator
and all of them are), but &(x..y)
is not.
You specifically want to bound &I
, which fortunately can be done via a where
clause, e.g.
impl<I, T> Bar<I> for Test
where for<'a> &'a I: IntoIterator<Item = T>
{
fn bar(&self, i: &I) {
for x in i {
println!("woo!");
}
}
}
The for<'a>
just means "for any lifetime 'a
", and so the where
clause is saying that &I
is always an IntoIterator
(just writing where &I: IntoIterator
isn't quite enough).
There's some choices to be made about the T
parameter there, e.g.
IntoIterator<Item = T>
IntoIterator<Item = &'a T>
IntoIterator
The best choice will depend on exactly what you're doing with it. For the specific example in the question, I would go with 3, since the Item
type doesn't matter at all. Number 2 makes sense because almost all types that have &T
implement IntoIterator
will yield references (it also seems to avoids most of the bugs/general difficulties the compiler currently has about reasoning about universal quantification over lifetimes, which hit 1 and 3).
Upvotes: 13