iter() vs into_iter()

In Rust, iter() and into_iter() are methods used to create iterators over collections, but they have distinct behaviors:

iter()

  • Borrowing Iterator: Returns an iterator that borrows references (&T) to the elements in the collection.
  • Non-consuming: The original collection remains unmodified after using iter(). You can iterate over the collection multiple times without any issues.
  • Read-only Access: You can only access the elements through the borrowed references, not modify them directly.


fn get_even(input_vec: &Vec<i32>) -> Vec<i32> {
    input_vec
        .iter()
        .filter(|&x| x%2 == 0)
        .map(|&x|x*x)
        .collect()
}

fn main() {
    let numbers = vec![1,2,3,4,5,6,7,8,9];
    
    let mut iter = numbers.iter(); // Iterator of references (&i32)
    
    while let Some(number) = iter.next() {
        println!("Number: {}", number); // Accessing through reference
    }

    // Original collection is still usable
    println!("Numbers: {:?}", numbers);
    
    println!("Even numbers {:?}",get_even(&numbers));
    println!("Numbers again : {:?}", numbers);
}
/*
Number: 1
Number: 2
Number: 3
Number: 4
Number: 5
Number: 6
Number: 7
Number: 8
Number: 9
Numbers: [1, 2, 3, 4, 5, 6, 7, 8, 9]
Even numbers [4, 16, 36, 64]
Numbers again : [1, 2, 3, 4, 5, 6, 7, 8, 9]
*/        

into_iter()

  • Owning Iterator: Returns an iterator that takes ownership of the elements in the collection and moves them into the iterator.
  • Consuming: Using into_iter() consumes the original collection. You cannot use the original collection after iterating over it with into_iter().
  • Mutable Access: You can modify the elements within the iterator because it owns them (ownership is transferred).

fn main() {
    let numbers = vec![1,2,3,4,5,6,7,8,9];
    
    let mut iter = numbers.into_iter(); // Iterator of references (&i32)
    
    while let Some(number) = iter.next() {
        println!("Number: {}", number); // Accessing through reference
    }

    // Original collection is still not usable
   // uncommenting will cause an error
   // println!("Numbers: {:?}", numbers);
}

// Below is the error if we consume the vec which is already called into_iter()

/*
13 |     let numbers = vec![1,2,3,4,5,6,7,8,9];
   |         ------- move occurs because `numbers` has type `Vec<i32>`, which does not implement the `Copy` trait
14 |     
15 |     let mut iter = numbers.into_iter(); // Iterator of references (&i32)
   |                            ----------- `numbers` moved due to this method call
...
22 |     println!("Numbers: {:?}", numbers);
   |                               ^^^^^^^ value borrowed here after move
   |
note: `into_iter` takes ownership of the receiver `self`, which moves `numbers`
  --> /rustc/9eb3afe9ebe9c7d2b84b71002d44f4a0edac95e0/library/core/src/iter/traits/collect.rs:262:18
   = note: this error originates in the macro `$crate::format_args_nl` which comes from the expansion of the macro `println` (in Nightly builds, run with -Z macro-backtrace for more info)
help: you can `clone` the value and consume it, but this might not be your desired behavior
*/        

Sinimarities betwee inetr() and into_iter()

Both iter() and into_iter() provide mechanisms for iterating over collections in Rust, but they differ in terms of ownership and mutability.

  • Both return iterators: Both methods return an iterator object that allows you to step through the elements of a collection one by one.
  • Both implement the Iterator trait: This trait defines the behavior of iterators, including the next() method that returns the next element and the Some and None variants to indicate whether there are more elements.
  • Both follow the iterator pattern: They provide a way to iterate over a collection element by element without explicitly accessing each element by index. This promotes cleaner and more concise code.
  • Can be used with the for loop: You can use both iter() and into_iter() with a for loop to iterate through the elements of a collection in a concise and readable manner.


Choosing Between iter() and into_iter()

  • Use iter() when you need to iterate over a collection without modifying its elements and want to keep the original collection intact.
  • Use into_iter() when you want to consume the collection and potentially modify the elements during iteration. This is useful for scenarios like:

1. Processing elements and discarding the original collection.

2. Transforming elements into a new collection (use collect() on the iterat


Iter() with collect() method :

The collect() method in Rust can be used with iter(), but there's a caveat.

Here's the breakdown:

iter() and Ownership:


  • iter() returns an iterator that borrows references (&T) to the elements in the collection. This means it doesn't take ownership of the elements.


collect() and Ownership:

  • collect() consumes the iterator and attempts to collect the elements into a new collection. It requires the iterator to yield elements that the new collection can own.


Compatibility:

  • Since iter() provides borrowed references, the new collection cannot directly own these references. However, there are workarounds:


into_iter() and collect():

  • into_iter() offers a more natural fit for collect() because it returns an iterator that owns the elements (T). This allows the new collection to directly take ownership of the elements during the collection process.

Here's an example to illustrate the concept:

fn main() {
    let numbers = vec![1,2,3,4,5,6,7,8,9];
    
    // Approach1: Not ideal (less common): collect references

    let refs: Vec<&i32> = numbers.iter().collect(); 

    // Needs a collection type that accepts references
    

    //  Approach2: More common approach: clone elements and collect

    let collected_using_iter: Vec<i32> = numbers.iter().cloned().collect(); 

    // Clone borrowed elements
    

    // Approach3: Collected using map to convert from &i.e referance to value 

    let collected_using_into_iter: Vec<i32> = numbers.().iter().map(|&x|x).collect()
  

   // Approach4: Ideal scenario for collect

    let collected_using_into_iter: Vec<i32> = numbers.into_iter().collect(); 

    // Ownership transferred
}        

While collect() can technically be used with iter() under certain conditions, into_iter() is generally the preferred and more efficient choice because it aligns better with ownership semantics and avoids complications with references.

Additional Notes:

  • Some collection types might offer additional iterator methods like iter_mut() which provides mutable references to elements while not consuming the collection.
  • When iterating over large collections, into_iter() might be more memory-efficient because it avoids creating copies of elements.

要查看或添加评论,请登录

社区洞察

其他会员也浏览了