(I just read earlier today that the following are supposedly equivalent.)
Is there any advantage of:
'for x in blah.iter()' over 'for x in &blah' or
'for x in blah.mut_iter()' over 'for x in &mut blah'
?
(I just read earlier today that the following are supposedly equivalent.)
Is there any advantage of:
'for x in blah.iter()' over 'for x in &blah' or
'for x in blah.mut_iter()' over 'for x in &mut blah'
?
There is normally no difference other than &blah
requiring less characters than blah.iter()
.
For example, the IntoIterator
implementation for &[T]
and &mut [T]
literally call iter()
and iter_mut()
.
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T> IntoIterator for &'a [T] {
type Item = &'a T;
type IntoIter = Iter<'a, T>;
fn into_iter(self) -> Iter<'a, T> {
self.iter()
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<'a, T> IntoIterator for &'a mut [T] {
type Item = &'a mut T;
type IntoIter = IterMut<'a, T>;
fn into_iter(self) -> IterMut<'a, T> {
self.iter_mut()
}
}
To be pedantic,
for _ in _
always calls IntoIterator
. But it just so happens that the impls for &[T], &mut [T]
call .iter() and .mut_iter()
?
Yes, that's correct. The for-loop will desugar into a <&[T] as IntoIterator>::into_iter(...)
call, which internally just calls <[T]>.iter()
.
Note that this is only a convention that pretty much all standard library types with iter()
-like methods follow. There is nothing stopping me from giving my type an iter()
method then doing something completely different in the impl IntoIterator for &MyType
impl.
Nitpick: iter
is a method on [T]
that takes &self
, not a method on &[T]
. You can call it on a value of type &[T]
because of the implicit dereferencing that takes place during method resolution.
Note that there's no reason why it couldn't be written the other way around: [T]::iter
could instead call <&[T]>::into_iter
. Or they could both be independently implemented to do the same thing without calling each other at all. That into_iter
calls iter
and not the other way around is no more than an implementation detail.
I prefer to think of a.iter()
as shorthand for (&a).into_iter()
because IntoIterator
is more fundamental, in a way, than the convention of naming a by-reference iteration method iter
.
It'd be nice if we could create some sort of Iter
trait which turns this convention into a pattern that can be relied on (e.g. implement IntoIterator
for references and get a iter()
method for free).
It seems like this requires GATs though, and when I tried it the closest I could get to was this generics soup:
#![feature(generic_associated_types)]
trait Iter {
type Iterator<'a>: Iterator + 'a;
type Item<'a>: 'a;
fn iter(&self) -> Self::Iterator<'_>;
}
impl<I> Iter for I
where
for<'a> &'a I: IntoIterator,
for<'a> <&'a I as IntoIterator>::IntoIter: Iterator,
{
type Iterator<'a> where I: 'a = <&'a I as IntoIterator>::IntoIter;
type Item<'a> where I: 'a = <&'a I as IntoIterator>::Item;
fn iter(&self) -> Self::Iterator<'_> {
IntoIterator::into_iter(self)
}
}
Which triggered some really weird compiler errors that I eventually created a ticket for.
You can make this work if you parameterize Iter
with a lifetime:
trait Iter<'a> {
type Iterator: Iterator<Item=&'a Self::Item> + 'a;
type Item: 'a;
fn iter(&'a self) -> Self::Iterator;
}
impl<'a, I: 'a, T: 'a> Iter<'a> for I
where
&'a I: IntoIterator<Item= &'a T>,
{
type Iterator = <&'a I as IntoIterator>::IntoIter;
type Item = T;
fn iter(&'a self) -> Self::Iterator {
IntoIterator::into_iter(self)
}
}
Or you can do something like this to capture the full convention:
trait Iterable: IntoIterator<Item = <Self as Iterable>::Item>
where
for<'a> &'a Self: IntoIterator<Item = &'a <Self as Iterable>::Item>,
for<'a> &'a mut Self: IntoIterator<Item = &'a mut <Self as Iterable>::Item>,
{
type Item;
fn iter(&self) -> <&'_ Self as IntoIterator>::IntoIter {
self.into_iter()
}
fn iter_mut(&mut self) -> <&'_ mut Self as IntoIterator>::IntoIter {
self.into_iter()
}
}
impl<T, I> Iterable for T
where
T: IntoIterator<Item = I>,
for<'a> &'a T: IntoIterator<Item = &'a I>,
for<'a> &'a mut T: IntoIterator<Item = &'a mut I>,
{
type Item = I;
}
I'd love something like this, though I wonder if it would be better to define a trait providing iter and a separate trait providing iter_mut, assuming there's cases where a data type (that doesn't have into_iter) would just want iter without iter_mut.
(Not intended as a criticism of your example, which demonstrates the concept efficiently)
If I were to build this out more, Iād probably make it a stack of traits similar to Fn{,Once,Mut}. So, IterMut
implies Iter
which in turn implies IntoIterator
, but immutable containers can have Iter
without IterMut
.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.