What is the difference between iter and into_iter?

后端 未结 4 892
情书的邮戳
情书的邮戳 2020-11-22 14:18

I am doing the Rust by Example tutorial which has this code snippet:

// Vec example
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];

// `iter()` for vecs         


        
相关标签:
4条回答
  • 2020-11-22 14:42

    TL;DR:

    • The iterator returned by into_iter may yield any of T, &T or &mut T, depending on the context.
    • The iterator returned by iter will yield &T, by convention.
    • The iterator returned by iter_mut will yield &mut T, by convention.

    The first question is: "What is into_iter?"

    into_iter comes from the IntoIterator trait:

    pub trait IntoIterator 
    where
        <Self::IntoIter as Iterator>::Item == Self::Item, 
    {
        type Item;
        type IntoIter: Iterator;
        fn into_iter(self) -> Self::IntoIter;
    }
    

    You implement this trait when you want to specify how a particular type is to be converted into an iterator. Most notably, if a type implements IntoIterator it can be used in a for loop.

    For example, Vec implements IntoIterator... thrice!

    impl<T> IntoIterator for Vec<T>
    impl<'a, T> IntoIterator for &'a Vec<T>
    impl<'a, T> IntoIterator for &'a mut Vec<T>
    

    Each variant is slightly different.

    This one consumes the Vec and its iterator yields values (T directly):

    impl<T> IntoIterator for Vec<T> {
        type Item = T;
        type IntoIter = IntoIter<T>;
    
        fn into_iter(mut self) -> IntoIter<T> { /* ... */ }
    }
    

    The other two take the vector by reference (don't be fooled by the signature of into_iter(self) because self is a reference in both cases) and their iterators will produce references to the elements inside Vec.

    This one yields immutable references:

    impl<'a, T> IntoIterator for &'a Vec<T> {
        type Item = &'a T;
        type IntoIter = slice::Iter<'a, T>;
    
        fn into_iter(self) -> slice::Iter<'a, T> { /* ... */ }
    }
    

    While this one yields mutable references:

    impl<'a, T> IntoIterator for &'a mut Vec<T> {
        type Item = &'a mut T;
        type IntoIter = slice::IterMut<'a, T>;
    
        fn into_iter(self) -> slice::IterMut<'a, T> { /* ... */ }
    }
    

    So:

    What is the difference between iter and into_iter?

    into_iter is a generic method to obtain an iterator, whether this iterator yields values, immutable references or mutable references is context dependent and can sometimes be surprising.

    iter and iter_mut are ad-hoc methods. Their return type is therefore independent of the context, and will conventionally be iterators yielding immutable references and mutable references, respectively.

    The author of the Rust by Example post illustrates the surprise coming from the dependence on the context (i.e., the type) on which into_iter is called, and is also compounding the problem by using the fact that:

    1. IntoIterator is not implemented for [T; N], only for &[T; N] and &mut [T; N]
    2. When a method is not implemented for a value, it is automatically searched for references to that value instead

    which is very surprising for into_iter since all types (except [T; N]) implement it for all 3 variations (value and references). It's not possible for the array to implement an iterator that yields values because it cannot "shrink" to give up its items.

    As to why arrays implement IntoIterator (in such a surprising fashion): it's to make it possible to iterate over references to them in for loops.

    0 讨论(0)
  • 2020-11-22 14:44

    .into_iter() is not implemented for a array itself, but only &[]. Compare:

    impl<'a, T> IntoIterator for &'a [T]
        type Item = &'a T
    

    with

    impl<T> IntoIterator for Vec<T>
        type Item = T
    

    Since IntoIterator is defined only on &[T], the slice itself cannot be dropped the same way as Vec when you use the values. (values cannot be moved out)

    Now, why that's the case is a different issues, and I'd like to learn myself. Speculating: array is the data itself, slice is only a view into it. In practice you cannot move the array as a value into another function, just pass a view of it, so you cannot consume it there either.

    0 讨论(0)
  • 2020-11-22 14:50

    I think there's something to clarify a bit more. Collection types, such as Vec<T> and VecDeque<T>, have into_iter method that yields T because they implement IntoIterator<Item=T>. There's nothing to stop us to create a type Foo<T> if which is iterated over, it will yield not T but another type U. That is, Foo<T> implements IntoIterator<Item=U>.

    In fact, there are some examples in std: &Path implements IntoIterator<Item=&OsStr> and &UnixListener implements IntoIterator<Item=Result<UnixStream>>.


    The difference between into_iter and iter

    Back to the original question on the difference between into_iter and iter. Similar to that others have pointed out, the difference is that into_iter is a required method of IntoIterator which can yield any type specified in IntoIterator::Item. Typically, if a type implements IntoIterator<Item=I>, by convention it has also two ad-hoc methods: iter and iter_mut which yield &I and &mut I, respectively.

    What it implies is that we can create a function that receives a type that has into_iter method (i.e. it is an iterable) by using a trait bound:

    fn process_iterable<I: IntoIterator>(iterable: I) {
        for item in iterable {
            // ...
        }
    }
    

    However, we can't* use a trait bound to require a type to have iter method or iter_mut method, because they're just conventions. We can say that into_iter is more widely useable than iter or iter_mut.

    Alternatives to iter and iter_mut

    Another interesting to observe is that iter is not the only way to get an iterator that yields &T. By convention (again), collection types SomeCollection<T> in std which have iter method also have their immutable reference types &SomeCollection<T> implement IntoIterator<Item=&T>. For example, &Vec<T> implements IntoIterator<Item=&T>, so it enables us to iterate over &Vec<T>:

    let v = vec![1, 2];
    
    // Below is equivalent to: `for item in v.iter() {`
    for item in &v {
        println!("{}", item);
    }
    

    If v.iter() is equivalent to &v in that both implement IntoIterator<Item=&T>, why then does Rust provide both? It's for ergonomics. In for loops, it's a bit more concise to use &v than v.iter(); but in other cases, v.iter() is a lot clearer than (&v).into_iter():

    let v = vec![1, 2];
    
    let a: Vec<i32> = v.iter().map(|x| x * x).collect();
    // Although above and below are equivalent, above is a lot clearer than below.
    let b: Vec<i32> = (&v).into_iter().map(|x| x * x).collect();
    

    Similarly, in for loops, v.iter_mut() can be replaced with &mut v:

    let mut v = vec![1, 2];
    
    // Below is equivalent to: `for item in v.iter_mut() {`
    for item in &mut v {
        *item *= 2;
    }
    

    When to provide (implement) into_iter and iter methods for a type

    If the type has only one “way” to be iterated over, we should implement both. However, if there are two ways or more it can be iterated over, we should instead provide an ad-hoc method for each way.

    For example, String provides neither into_iter nor iter because there are two ways to iterate it: to iterate its representation in bytes or to iterate its representation in characters. Instead, it provides two methods: bytes for iterating the bytes and chars for iterating the characters, as alternatives to iter method.


    * Well, technically we can do it by creating a trait. But then we need to impl that trait for each type we want to use. Meanwhile, many types in std already implement IntoIterator.

    0 讨论(0)
  • 2020-11-22 14:51

    I (a Rust newbie) came here from Google seeking a simple answer which wasn't provided by the other answers. Here's that simple answer:

    • iter() iterates over the items by reference
    • into_iter() iterates over the items, moving them into the new scope
    • iter_mut() iterates over the items, giving a mutable reference to each item

    So for x in my_vec { ... } is essentially equivalent to my_vec.into_iter().for_each(|x| ... ) - both move the elements of my_vec into the ... scope.

    If you just need to "look at" the data, use iter, if you need to edit/mutate it, use iter_mut, and if you need to give it a new owner, use into_iter.

    This was helpful: http://hermanradtke.com/2015/06/22/effectively-using-iterators-in-rust.html

    Making this a community wiki so that hopefully a Rust pro can edit this answer if I've made any mistakes.

    0 讨论(0)
提交回复
热议问题