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.

 

 

 

 

 

___________

Kategoriler: BlockchainRust

0 yorum

Bir cevap yazın

Avatar placeholder

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

This site uses Akismet to reduce spam. Learn how your comment data is processed.