2

I can implement Iterator trait for my struct, and get IntoIterator implemented for free, thanks to the blanket implementation.

struct Foo {}

impl Iterator for Foo {
    type Item = ();
    fn next(&mut self) -> Option<Self::Item> { None }
}

fn bar(foo: Foo) {
    foo.into_iter(); // implemented automatically for my struct
}

But why can I also do this?

fn bar(foo: &mut Foo) {
    foo.into_iter(); // works; but shouldn't into_iter require ownership?
}

The implementation of the into_iter method seems to require ownership of the value:

impl<I: Iterator> const IntoIterator for I {
    type Item = I::Item;
    type IntoIter = I;

    #[inline]
    fn into_iter(self) -> I {
        self
    }
}

What allows the method into_iter to accept mutable reference when it is defined to take ownership?

Is there a way to make the method move_out in the following example to behave like the method into_iter?

trait A {}
trait B {
    fn move_out(self) -> Self;
}

impl<T: A> B for T {
    fn move_out(self) -> Self {
        self
    }
}

struct Foo {}
impl A for Foo {}

fn bar(foo: &mut Foo) {
    foo.move_out(); // compile error
}
Alexander
  • 103
  • 2
  • 8
  • 1
    I'm not confident enough to answer, but I believe the two pieces needed to understand this are the [blanket implementation of `Iterator` for `&mut I`](https://doc.rust-lang.org/stable/std/iter/trait.Iterator.html#impl-Iterator-for-%26mut%20I) and [reborrowing](https://stackoverflow.com/questions/65474162/reborrowing-of-mutable-reference). `foo.into_iter()` is equivalent to `IntoIterator::into_iter(&mut *foo)` which calls the `&mut I` blanket implementation and reborrows the `&mut` ref so it remains usable afterwards. – John Kugelman Apr 15 '23 at 14:48
  • 1
    If you use clippy, it should warn you about using `into_iter()` “incorrectly”: https://rust-lang.github.io/rust-clippy/master/index.html#into_iter_on_ref – BallpointBen Apr 16 '23 at 16:25

1 Answers1

2

It's pretty straight forward any type T that implements Iterator also implements Iterator for &mut T. And all types implementing Iterator implement IntoIterator which allows you to call into_iter on them in the first place.

You can make A and B behave the same way by simply adding the same blanket implementation Iterator has:

impl<T: A> A for &mut T {}

Playground

cafce25
  • 15,907
  • 4
  • 25
  • 31