Reputation: 98
I was trying to use take(n)
on an iterator to do something with the first n
items, but then do something different (a for ... in ...
loop) with the remaining items.
In the declaration:
fn take(self, n: usize) -> Take<Self>
the self
as first argument tells me that take()
must be used on an owned value, not a reference (&self
) or mutable reference (&mut self
).
The documentation for by_ref()
says it returns a mutable reference, and
This is useful to allow applying iterator adaptors while still retaining ownership of the original iterator.
It includes an example using by_ref().take(n)
, and then continuing to use the iterator (just like I wanted).
My question is, why am I allowed to call take()
on a mutable reference (as opposed to requiring an owned value)? In other words, shouldn't take()
have been declared something like:
fn take(&mut self, n: usize) -> Take<Self>
to make it possible to use it with a mutable reference?
What declaration should I have been looking for that would have told me this was possible?
Upvotes: 3
Views: 444
Reputation: 15135
Rust treats T
, &T
, and &mut T
as separate types. Being separate types we can implement different methods for each of them. Example:
struct Struct;
trait Printable {
fn print(self);
}
impl Printable for Struct {
fn print(self) {
println!("I'm an owned Struct");
}
}
impl Printable for &Struct {
fn print(self) {
println!("I'm a Struct reference");
}
}
fn main() {
let s = Struct;
let s_ref = &Struct;
s.print();
s_ref.print();
}
If we desugar the trait method we get:
fn print(self: Self) { /* implementation */ }
Where Self
is equal to the implementing type, which in the case of Struct
is:
fn print(self: Struct) { /* implementation */ }
And in the case of &Struct
is:
fn print(self: &Struct) { /* implementation */ }
So really self
can be an owned type or an immutable reference or a mutable reference. The reason why you can call take
on mutable references to Iterator
s is because of this generic blanket impl which implements the Iterator
trait on all mutable references to Iterator
s:
impl<'_, I> Iterator for &'_ mut I where I: Iterator + ?Sized { /* implementation */ }
Let's use vec.into_iter()
as a concrete example. Since it returns std::vec::IntoIter
which implements Iterator
we know this implementation of take
must exist:
take(self: std::vec::IntoIter, n: usize) -> Take<std::vec::IntoIter> { /* implementation */ }
However, we also know that an implementation of take
for &mut std::vec::IntoIter
must exist because it would be automatically generated by the generic blanket impl mentioned above, and that implementation's signature would look like this:
take(self: &mut std::vec::IntoIter, n: usize) -> Take<&mut std::vec::IntoIter> { /* implementation */ }
And this why you can call take
on any mutable reference to any type that implements Iterator
.
Upvotes: 7