I have a type A and I implemented the IntoIterator trait on &A. Now I have the following snippet:
let a = A::new();
let mut iter = a.into_iter();
loop
{
match iter.next() {
Some(val) => println!("value {}", val),
None => break
}
}
for i in &a
{
println!("for loop value {}", i);
}
The compiler seems to invoke the into_iter() implemented on the reference type A. Can I infer that in the absence of the implementation of the IntoIterator trait on A the compiler will pick the implementation on the reference?
Mostly yes. The exact rules are stated here in the Rust reference.
The gist is that during a method call resolution, the type of the receiver object, here A, is both referenced and de-referenced. There is a specific ordering, which is not super useful unless you want to answer David Tolnay's quiz correctly. Hence, although a is of type A, since the method into_iter exists for &A, it is picked up.
This is no different from you calling an impl method on something which is the type itself, not a reference.
You haven’t implemented the Trait method into_iter for your owned type OR you haven’t created an iter method for you collection (collection -> a type that implements Iterator). If you rely on inference, you need 3 versions of into_iter (3 separate Trait impls); the inference for which (collection -> iterator) is then made based on the rights you have with the collection (encoded in the type and used to lookup the appropriate trait implementation).
The into_iter Trait method for each of owned, exclusive borrow and borrow all take ownership (per the into_iter naming convention semantic move into), of the value, exclusive ref and non-exclusive ref respectively* [1].
Separately, you can opt to create methods: into_iter, iter_mut and iter in a impl block of your collection [2].
Here is a link to a document that describes iterators many found useful. It describes the different ways for building an iterator for your collection.
Another important piece of information is that it’s not really important what the Self-type of the impl … or impl …trait… for … block is, but only what the type of the self argument is.
I.e. there is no special rule that only fn …(&self, …) methods or fn …(&mut self, …) methods in an impl Foo or impl SomeTrait for Foo block can be called on a variable x: Foo, but - as demonstrated by the IntoIterator example, it could also be a fn …(self, …) method in an impl SomeTrait for &Foo (or some indirectly / generically implemented trait).
This also means there is - in practice - a backwards compatibility problem with adding such an impl for A later. Now, the documented rules for what’s considered “breaking” changes allow breakage related to method resolution AFAIR, but it’s still able to cause problems: one particular example was the IntoIterator impl for [T; N] that was added to the standard library. Breakage to existing foo.into_iter() calls on foo: [T; N] that used to produce slice::Iter<'_, T> iterators of &foo was considered a large enough concern that special-case rules (modifying method resolution) were introduced to the compiler that kept the old behavior for into_iter on arrays in all but the latest 2021 edition.
As an aside, note that it's very weird behavior for a .into_*() call to not consume the receiver, in that it's at least at the surface inconsistent with naming conventions.
It's not inherent to that example though, more of a side effect of how the IntoIterator trait works.