What are iterators in Rust?
Anything which implements IntoIterator trait called as an iterator. Vec struct is implements iterator and all vectors are actually an iterator. And for loops are uses iterators in background.
let v = vec![1, 2, 3, 4]; for num in v { println!("num: {}", num); }
for loops are actually uses iterators but Vec struct isn’t an iterator. If something isn’t iterator then for loop calls into_iter() method. The actual code is will be like that:
let v = vec![1, 2, 3, 4]; for num in v.into_iter() { println!("num: {}", num); }
The into_iter() method returns an iterator which takes ownership of the collection and in for loop the collection will be consumed and after loop it will gone.
But you don’t need to use for loop for using vectors. You can manipulate and handle all values one by one with functions which comes from IntoIterator trait. Let’s look to these.
Iterator Adapters
map() is used to transform each item in some way. You give map a closure that takes ownership of the value, and whatever the closure return becomes the new value. Map can return a different type than it received but in this example we stick with integers for simplicity.
let v = vec![6, 7, 8]; let total = v.into_iter().map(|num| num * 3);
filter() adapter to remove some values that don’t meet a condition. filter() takes a closure that receives an immutable reference to the value, because we don’t need to change the original value. And for accessing the value you must dereference it. We just want to filter each value by a condition and create new iterator which contains all values which passes by condition.
let total = v.into_iter() .map(|num: i32| num * 3) // takes ownership of value .filter(|num: &i32| *num % 2 == 0); // takes reference of value, use *num
You can look to rust documentation for learning what types these closures arguments and what the return value of the closure.
Iterator Consumers
An iterator consumer is something that actually consumes the final iterator in some way, causing the chain of iterator adapters to do their processing. These are some iterator consumer methods: for_each, sum, collect…
let total: i32 = v.into_iter() .map(|num: i32| num * 3) // takes ownership of value .filter(|num: &i32| *num % 2 == 0) // takes reference of value, use *num .sum::<i32>();
You must define result type of define the return value type with turbo fish syntax.
// using turbofish syntax for defining return value let total = v.into_iter() .map(|num: i32| num * 3) // takes ownership of value .filter(|num: &i32| *num % 2 == 0) // takes reference of value, use *num .collect::Vec<i32>(); // using type definition for receiving correct value let total: Vec<i32> = v.into_iter() .map(|num: i32| num * 3) // takes ownership of value .filter(|num: &i32| *num % 2 == 0) // takes reference of value, use *num .collect(); // for_each() method is another consumer method too. v.into_iter().for_each(|num| println!("Current number: {}", num));
Mutable or Immutable References of Iterators
You can access iterators as mutable or immutable references. For every purpose you can use different methods:
v.into_iter() // consumes v, returns owned items, v gone after this line (or after loop) v.iter() // returns immutable references v.iter_mut() // returns mutable references
You can use both of them in for loop of course.
for num in v.iter() { println!("num: {}", num); } // You must define mutable vector for changing items. let mut v = vec![1, 2, 3, 4, 5]; for num in v.iter_mut() { *num = num * 5; println!("num: {}", num); }
Also you can use syntactic sugar syntax instead of these methods.
v.into_iter() ➡️ for _ in v v.iter() ➡️ for _ in &v v.iter_mut() ➡️ for _ in &mut v
TODO: Look to drain() method in iterators and hashmaps.
___________
0 yorum