about
Bits_Iter Rust
08/01/2024
0
Bits Repo Code Bits Repo Docs

Bits: Rust Iteration

iterators, for-in loops, iterable custom types, iterator methods

Synopsis:

This bit demonstrates uses of Rust iterators to walk through enumerable collections. The purpose is to quickly acquire some familiarity with Rust iteration.
  • Rust iterable containers support two functions that return iterators: iter() for non-modifying traversals and iter_mut() for modifying traversals.
  • Iterator iter() supports the function fn next(&self) -> Option<Self::Item>.
  • Iterator iter_mut() supports fn next(&mut self) -> Option<Self::Item>.
  • While walking the collection next() returns Some(item). When the end of its collection is reached, next() returns None.
  • Collections may also support the IntoIterator trait with function into_iter(self) which consumes the collection and returns an iterator over its elements.
  • Rust Strings are a special case. Unlike vec and slice &T[], string items are not all the same size. strings hold utf-8 characters which vary in size from 1 to 4 bytes. So strings support two iterators: chars(&self), for moving from character to character, and bytes(&self) for stepping through each of the string's bytes.
  • Rust for-loops are simplifying syntax wrappers around iterators.
Demo Notes  
All of the languages covered in this demonstrtion support iteration using iterators. Rust iterators are provided by enumerable containers and have public interface with the method fn next(&self) -> Option<Self::Item>. This demonstration illustrates use of iterators with a series of functions that provide code, in several forms, for iterating through containers. It includes demos for library types and for a user-defined point class.

1.0 Iterators

Iteration is a process of stepping through a collection C of values for display and/or modification. One basic way to do that is to use a loop that calls next() on an iterator provided to it.
Iterators use the syntax:
itr.next() -> Option<T>
to step through a collection C. The return value is either Some(val) or None. The term val is the value of the next item in an instance of C. None indicates that there are no more items to view.
The instance itr is an iterator returned by c ε C by invoking one of the three functions:
c.into_iter() Creates an instance of an iterator provided by C and consumes c.
Elements of c can be mutated and used during iteration but c will be invalid after the call.
c.iter() Returns an iterator without consuming c.
Elements of c cannot be mutated.
c.iter_mut() Returns an iterator without consuming c.
Elements of c can be mutated.

2.0 Source Code

Code is shown in a series of blocks starting with basic iteration forms and building up to discussion of iterators for custom types.

2.1 Loop Iteration over Vec<T>

This block illustrates iteration over the items of v, a std::Vec<T>, using a simple loop. Outputs are displayed within the loop and again after the loop using println!(v). The debug formatter {:?} also uses an iterator to display the vector contents in a slightly different format.
/*---------------------------------------------------------
  Demo Vec iteration with loop construct
  - illustrates how iteration works, using most basic
    syntax operating on Vec<T> instances.
*/
fn demo_loop_iteration() {
  show_label("basic loop iteration with Vec", 35);

  /* 1. v.iter(), iterates without consuming v */
  let v = vec![1, 2, 3, 2, 1];
  show_op("Vec iteration using loop and iter()");
  let mut itr = v.iter();
  loop {
    match itr.next() {
      Some(item) => print!("{item} "),
      None => break,
    }
  }
  // next statement is valid since v not consumed
  println!();
  println!("{v:?}");

  /* 2. v.iter_mut() iterates and mutates without consuming v */
  let mut v = vec![1, 2, 3, 2, 1];
  let mut mitr = v.iter_mut();
  show_op("mutable vec iteration with loop and mut_iter()");
  loop {
    match mitr.next() {
      Some(item) => { *item += 1; print!("{item} "); }
      None => break,
    }
  }
  println!();
  println!("{v:?}");

  /* 3. v.into_iter() consumes v while converting to iterator */
  let mut itr = v.into_iter();
  show_op("generate iterator with v.into_iter()");
  loop {
    match itr.next() {
      Some(item) => print!("{item} "),
      None => break,
    }
  }
  // into_iter() consumes v so the next statement is invalid
  // println!("{v:?}");  // v was moved
  println!();

}




  -----------------------------------
  basic loop iteration with Vec
  -----------------------------------



  --- Vec iteration using loop and iter() ---
  1 2 3 2 1
  [1, 2, 3, 2, 1]












  --- mutable vec iteration with loop and mut_iter() ---
  2 3 4 3 2
  [2, 3, 4, 3, 2]









  --- generate iterator with v.into_iter() ---
  2 3 4 3 2

2.2 Iteration with for-in Loops

For collections c ε C, for-loop iterations usually take one of the forms:
for item in c {
   /* do something with item */
}
for expects an iterator after in. Since collection c is not an iterator, for-in invokes C::into_iterator(). That consumes c.
for item in &c {
   /* do something with item */
}
Since reference &c is not an iterator, for-in invokes &C::into_iterator(). That does not consume c.
for item in &mut c {
   /* do something that mutates item */
}
Since &mut c is not an iterator, for-in invokes &mut C::into_iterator(). That does not consume c.
The first three demonstrations in the block below use iterators directly to show what for-in expects. The last three demonstrations use the three forms presented in the table above.
/*---------------------------------------------------------
  Demo iteration with for-in loop construct
  - illustrates how for-in works, using idiomatic
    syntax operating on Vec<T> instances.
*/
fn demo_for_iteration() {
  show_label("basic for-in loop iteration using Vec", 45);

  /* 1. v.iter(), iterates without consuming v */
  let v = vec![1, 2, 3, 2, 1];
  show_op("Vec iteration using v.iter()");
  for item in v.iter() {
      print!("{item} ");
  }
  // next statement is valid since v not consumed
  println!();
  println!("{v:?}");

  /* 2. v.iter_mut() iterates and mutates without consuming v */
  let mut v = vec![1, 2, 3, 2, 1];
  show_op("mutable vec iteration using v.iter_mut()");
  println!("original:   {v:?}");
  for item in v.iter_mut() {
    *item += 1;
      print!("{item:?} ");
  }
  println!();
  println!("after iter: {v:?}");

  /* 3. v.into_iter() consumes v while converting to iterator */
  // let mut itr = v.into_iter();
  show_op("for-in uses v.into_iter()");
  for item in v.into_iter() {
    print!("{item:?} ");
  }
  // into_iter() consumes v so the next statement is invalid
  // println!("{v:?}");  // v was moved
  println!();
  /*
     4. iteration with for-in consumes v
        - same as 3. except that into_iter() is used implicitly
        - used in preference to 3
  */
  let v = vec![1, 2, 3, 4, 5];
  show_op("for-in uses v => into_iter()");
  for item in v {  // implicitly uses into_iter()
    print!("{item:?} ");
  }
  // into_iter() consumes v so the next statement is invalid
  // println!("{v:?}");  // v was moved
  println!();

  /*
     5. iteration over elements of v using &v
        - uses internal call to into_iter() implemented with
          Vec::iter() so v not moved
  */
  let v = vec![1, 2, -1, -2, 0];
  show_op("for-in uses &v => iter()");
  for item in &v {
    print!("{item:?} ");
  }
  println!();
  println!("{v:?}");  // v was not moved
  /*
     6. mutating iteration over elements of v using &mut v
        - generates into_iter() implemented with internal
          call to iter_mut(), so does not move v
  */
  let mut v = vec![1, 2, -1, -2, 0];
  show_op("for-in uses &mut v => iter_mut()");
  println!("original: {v:?}");
  for item in &mut v {
    *item += 1;
    print!("{item:?} ");
  }
  println!();
  println!("modified: {v:?}");  // v was not moved

  /*-------------------------------------------------------
    Iteration forms 4, 5, and 6 are the preferred useage.
    Forms 1, 2, and 3 show how for-in loops work.
  -------------------------------------------------------*/
}

                  

                  
                  
---------------------------------------------
  basic for-in loop iteration using Vec
---------------------------------------------


--- Vec iteration using v.iter() ---
1 2 3 2 1
[1, 2, 3, 2, 1]







--- mutable vec iteration using v.iter_mut() ---
original:   [1, 2, 3, 2, 1]
2 3 4 3 2
after iter: [2, 3, 4, 3, 2]







--- for-in uses v.into_iter() ---
2 3 4 3 2











--- for-in uses v => into_iter() ---
1 2 3 4 5












--- for-in uses &v => iter() ---
1 2 -1 -2 0
[1, 2, -1, -2, 0]









--- for-in uses &mut v => iter_mut() ---
original: [1, 2, -1, -2, 0]
2 3 0 -1 1
modified: [2, 3, 0, -1, 1]

2.3 Generate CSL for Several Different Types

Comma seperated lists (CSL) are a visually appealing way to show values in a collection. To provide that format either the first or the last item has to be formatted differently than the rest to avoid a leading or trailing comma. This code block uses three different strategies to properly format the list. The last shown uses an iterator method enumerate() to provide formatting in an idiomatic way. That method is one of several discussed in the Section 2.6, below.
/*---------------------------------------------------------
  Demonstrate iter() by displaying a comma seperated
  list (csl) of items in several common collections.
  - three different strategies used for making
    display comma-seperated
  - syntax used in this demo
    - iter().next() -> Option<Self::Item>
    - enum Option<T> { Some(T), None, }
*/
fn demo_iter() {
  show_label("demo_iter()", 20);

  /*--------------------------------------------------
    iterate over array
    csl strategy #1 extracts first item before iterating
  --------------------------------------------------*/
  show_op("array iter using loop");
  let ar = [1, 2, 3, 4];
  let mut iter = ar.iter();  // extract first item
  if let Some(item) = iter.next() {
    print!("{item}");
  }
  loop {
    let item = iter.next();
    match item {  //display remaining items
      Some(item) => print!(", {}", item),
      None => break
    }
  }
  println!();
  // ar not consumed by ar.iter(), above
  // so statement below is valid:
  println!("using println!:\n{:?}", ar);

  /*--------------------------------------------------
    iterate over Vec
    csl strategy #2 uses first flag
  --------------------------------------------------*/
  /*--- functionaly equivalent to loop, above ---*/
  show_op("Vec iter using for-in");
  let v = vec![1, 2, 3, 4];
  let mut first = true;   // set first flag
  for item in &v {
    if first {
      print!("{item}");
      first = false;            // reset first flag
    }
    else {
      print!(", {item}");
    }
  }
  println!();
  // statement below is valid, v not consumed since
  //   for-in used reference &v
  println!("using println!:\n{:?}", v);

  /*--------------------------------------------------
    iterate over HashMap
    csl strategy #3 uses enumerate()
  --------------------------------------------------*/
  show_op("HashMap iter using for-in");
  let mut hm = HashMap::<&str, i32>::new();
  hm.insert("zero", 0);
  hm.insert("one", 1);
  hm.insert("two", 2);
  hm.insert("three", 3);
  /*-------------------------------------------------------
    enumerate is an iterator adapter that returns another
    iterator yielding (count, value) where value is
    yielded by iter
  */
  for (count, item) in hm.iter().enumerate() {
    if count == 0 {
      print!("{item:?}");
    }
    else {
      print!(", {item:?}");
    }
  }
  println!();
  println!("using println!:\n{:?}", hm);

  /*--------------------------------------------------
    iterate over Point<T, N>,
    csl strategy same as above
  --------------------------------------------------*/
  show_op("Point iter using for-in");
  let mut p = Point::<f64, 5>::new();
  p.init(&vec![1.0, 1.5, 2.0, 1.5, 1.0]);
  for item in p.iter().enumerate() {
    if item.0 == 0 {  // count == zero
      print!("{:?}", item.1);
    }
    else {  // count > 0
      print!(", {:?}", item.1);
    }
  }
  println!();
  print!("using println!:\n{p:?}");  // p not moved
  println!("\n");

  /*--------------------------------------------------
    Use formatting function that accepts any type
    implementing IntoIterator trait.
    - function defined below
  --------------------------------------------------*/
  show_op("using show_csl(&ar) for array");
  show_csl(&ar); // ar not consumed
  show_op("using show_csl(&v) for Vector");
  show_csl(&v);  // v not consumed
  show_op("using show_csl(&hm) for HashMap");
  show_csl(&hm); // hm not consumed
  show_op("using show_csl(&p) for Point");
  show_csl(&p);  // p not consumed
  println!();

  show_op("using show_csl(ar) for array - copies ar");
  show_csl(ar);  // ar is not consumed as it is a copy type
  show_op("using show_csl(v) for Vector - moves v");
  show_csl(v);   // v is consumed
  show_op("using show_csl(hm) for HashMap - moves hm");
  show_csl(hm);  // hm is consumed
  show_op("using show_csl(p) for Point - moves p");
  show_csl(p);   // p is consumed
}

/* generalize csl strategy #3 */
fn show_csl<C>(c:C)  // consumes c
  where C: IntoIterator, C::Item: Debug
{
  let iter = c.into_iter();
  for (count, val) in iter.enumerate() {
    if count == 0 {
      print!("{:?}", val);
    }
    else {
      print!(", {:?}", val);
    }
  }
  println!();
}









--------------------
  demo_iter()
--------------------




--- array iter using loop ---
1, 2, 3, 4
using println!:
[1, 2, 3, 4]



















--- Vec iter using for-in ---
1, 2, 3, 4
using println!:
[1, 2, 3, 4]

















--- HashMap iter using for-in ---
("one", 1), ("three", 3), ("zero", 0), ("two", 2)
using println!:
{"one": 1, "three": 3, "zero": 0, "two": 2}






















--- Point iter using for-in ---
1.0, 1.5, 2.0, 1.5, 1.0
using println!:
Point { items: [1.0, 1.5, 2.0, 1.5, 1.0] }
















--- using show_csl(&ar) for array ---
1, 2, 3, 4
--- using show_csl(&v) for Vector ---
1, 2, 3, 4
--- using show_csl(&hm) for HashMap ---
("one", 1), ("three", 3), ("zero", 0), ("two", 2)
--- using show_csl(&p) for Point ---
1.0, 1.5, 2.0, 1.5, 1.0


--- using show_csl(ar) for array - copies ar ---
1, 2, 3, 4
--- using show_csl(v) for Vector - moves v ---
1, 2, 3, 4
--- using show_csl(hm) for HashMap - moves hm ---
("one", 1), ("three", 3), ("zero", 0), ("two", 2)
--- using show_csl(p) for Point - moves p ---
1.0, 1.5, 2.0, 1.5, 1.0

2.4 Iteration over custom Point<T, N> Coordinates

A custom type can be implemented to support iterators in the same way as for std::library types. Code in this section illustates how to do that. Essentially, the type must provide methods:
iter(), iter_mut() and implement the IntoIterator trait for Point<T, N> which provides the into_iterator() method.
Usually it will also implement IntoIterator for &Point<T, N> and for &mut Point<T, N> and non-mutable and mutable indexers using the std::ops module. See commentary below for motivation about adding these traits and operations.
This block defines a Point<T, N> type that contains a datum: items: Vec<T> to represent a point in N-dimensional hyperspace. It implements all of the features cited in the previous paragraph.
/*-- Point<T, N> --------------------------------
  Point<T, N> declares a Point type holding a
  Vec<T> of coordinate values.
  It implements:
  - new(n)  constructor
  - iter()  returns iterator over items
  - iter_mut() mutates while iterating
  - trait IntoIterator for Point<T, N>
  - trait IntoIterator for &Point<T, N>
  - trati IntoIterator for &mut Point<T, N>
  - immutable and mutable indexing
  Note:
  ---------------------------------------------
  This is a nice example of building a custom
  collection type. It implements methods and
  traits necessary to make a collection behave
  like standard library collections.
  ---------------------------------------------
*/
use std::fmt::*;

#[derive(Debug, Clone)]
pub struct Point<T, const N: usize>
    where T:Debug + Default + Clone
{
    pub items: Vec<T>
}
impl<T, const N:usize> Point<T, N>
    where T:Debug + Default + Clone
{
    /*-- constructor --*/
    pub fn new() -> Point<T, N> {
        Point::<T, N> {
            items: vec![T::default(); N],
        }
    }
    pub fn init(&mut self, v:&Vec<T>) {
      for i in 0..v.len() {
        self.items[i] = v[i].clone();
      }
      for i in v.len()..N {
        self.items[i] = T::default();
      }
    }
    /*-- non-destructive non-mutating iterator */
    pub fn iter(&self) -> impl Iterator<Item = &T> {
        self.items.iter()
    }
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
        self.items.iter_mut()
    }
}
/*-- implements const indexer -----------------*/
impl<T, const N:usize, Idx> std::ops::Index<Idx> for Point<T, N>
    where
        T:Debug + Default + Clone,
        Idx: std::slice::SliceIndex<[T]>
{
    type Output = Idx::Output;

    fn index(&self, index:Idx) -> &Self::Output {
        &self.items[index]
    }
}
/*-- implements mutable indexer ---------------*/
impl<T, const N:usize, Idx> std::ops::IndexMut<Idx> for Point<T, N>
    where
        T:Debug + Default + Clone,
        Idx: std::slice::SliceIndex<[T]>
{
    fn index_mut(&mut self, index:Idx) -> &mut Self::Output {
        &mut self.items[index]
    }
}
/*-- IntoIterator trait for PointN<T> ---------*/
impl<T, const N:usize> IntoIterator for Point<T, N>
    where T:Debug + Default + Clone
{
    type Item = T;
    type IntoIter = std::vec::IntoIter<Self::Item>;
    fn into_iter(self) -> Self::IntoIter {
        self.items.into_iter()
    }
}
/*-- IntoIterator trait for &Point<T, N> -------------
  - Supports interating elements of Point
  - Point instance is not moved because we use
    Vec::iter() internally
  - a is a required lifetime annotation
*/
use core::slice::Iter;

impl<'a, T, const N:usize> IntoIterator for &'a Point<T, N>
    where T:Debug + Default + Clone
{
    type Item = &'a T;
    type IntoIter = Iter<'a, T>;
    fn into_iter(self) -> Self::IntoIter {
        self.items.iter()
    }
}
/*-- IntoIterator trait for &mut Point<T, N> ---------
  - Supports mutating elements of Point while
    iterating. No clone used here.
  - Point instance is not moved because we use
    Vec::iter_mut() internally
  - a is a required lifetime annotation
*/
use core::slice::IterMut;

impl<'a, T, const N:usize> IntoIterator for &'a mut Point<T, N>
    where T:Debug + Default + Clone
{
    type Item = &'a mut T;
    type IntoIter = IterMut<'a, T>;
    fn into_iter(self) -> Self::IntoIter {
        self.items.iter_mut()
    }
}

Point<T, N> Definition:

Point<T, N> instances are collections of coordinates. 
We use this custom type to show how to implement 
many of the important methods and traits for col-
lections so it can be used like std library collections.
















Point<T, N> has a generic parameter T, the type of its
coordinates, and N the number of coordinates it uses.

Its coordinate values are held in a Vec<T>, so instances 
can be moved, but not implicitly copied. It declares a 
derived Clone, asking the compiler to implement that 
trait for Point.

 Point Methods:


new() -> Point<T, N> constructs a new instance with 
default values for items.




init(&mut self, v:&Vec<T>) fills the item coordinates 
with values from v.








iter(&self) -> impl Iterator<Item = &T> returns
items vector iterator.

iter_mut(&mut self) -> impl Iterator<Item = &mut T>
returns items mutating iterator.

Note: impl [trait] in the return means that any type 
can be returned if it implements the specified trait.


 non-mutating indexer
uses items vector non-mutating indexer.











 mutating indexer
uses items vector mutating indexer.








 IntoIterator for Point<T, N>
implements the IntoIterator trait for Point<T, N>.
Invocation of its trait function p.into_iter() on 
p ε Point<T, N> returns an iterator over p's items, 
consuming p so it is no longer valid.









 IntoIterator for &Point<T, N>
implements the IntoIterator trait for &Point<T, N>.
Invocation of its trait function &p.into_iter() on 
p ε Point<T, N> does not consume p so it remains valid.


















 IntoIterator for &mut Point<T, N>
implements IntoIterator trait for &mut Point<T, N>.
Invocation of its trait function &mut p.into_iter() on 
p ε Point<T, N> does not consume p so it remains valid.


2.5 Point<T, N> Demonstration

/*---------------------------------------------------------
  Demo iteration over coordinate values in Point<T,N>
  - illustrates how to implement iteration for custom
    types, using definitions in points_iter.rs module.
*/
fn demo_point_iteration() {

  show_label("demo point iteration", 30);

  /* uses Point<T,N>::IntoIterator => into_iter() => move */
  let mut p = Point::<i32, 5>::new();
  p.init(&vec![3, 2, 1, 0, -1]);
  show_op("for-in uses p, generating iter from p.into_iter()");
  for item in p {
    print!("{item:?} ");
  }
  // into_iter() consumes v so the next statement is invalid
  // println!("{p:?}");  // v was moved
  println!();

  /* uses &Point<T,N>::IntoIterator => iter() => no move */
  let mut p = Point::<i32, 5>::new();
  p.init(&vec![3, 2, 1, 0, -1]);
  show_op("for-in uses &p, generating iter from &p.iter()");
  println!("original:   {p:?}");
  for item in &p {
    print!("{item:?} ");
  }
  println!();
  println!("after iter: {p:?}");  // v was not moved

  /* uses &mut Point<T,N>::IntoIterator => iter_mut() => no move */
  let mut p = Point::<i32, 5>::new();
  p.init(&vec![-3, -2, -1, 0, 1]);
  show_op("for-in uses &mut p, generating iter from &mut p.iter_mut()");
  println!("original:  {:?}", p);
  for item in &mut p {
    *item += 1;
    print!("{item:?} ");
  }
  println!();
  println!("modified:  {p:?}");  // v was not moved

}






------------------------------
  demo point iteration
------------------------------



--- for-in uses p, generating iter from p.into_iter() ---
3 2 1 0 -1









--- for-in uses &p, generating iter from &p.iter() ---
original:   Point { items: [3, 2, 1, 0, -1] }
3 2 1 0 -1
after iter: Point { items: [3, 2, 1, 0, -1] }







--- for-in uses &mut p, generating iter from &mut p.iter_mut() ---       
original:  Point { items: [-3, -2, -1, 0, 1] }
-2 -1 0 1 2
modified:  Point { items: [-2, -1, 0, 1, 2] }

2.6 Iterator Methods

All Rust iterators provide methods that return another iterator, a collection, or a value. The table, below, provides a brief selection of methods that are used in a demonstration below the table.
Selection of iterator methods from std::Iter
all<F> (&mut self, f: F) -> bool
where Self: Sized, F: FnMut(Self::Item) -> bool
Tests if every item of the iterator matches a predicate.
any<F> (&mut self, f: F) -> bool
where Self: Sized, F: FnMut(Self::Item) -> bool
Tests if any item of the iterator matches a predicate.
cloned<'a T> (self) -> Cloned<Self>
where T: 'a + Clone', Self: Sized + Iterator<Item = &'a T>"
Consumes an iterator to create an iterator that clones all of its elements.
collect<B> (self) -> B
where B: FromIterator<Self::Item>, Self: Sized
Transforms an iterator into a collection, consuming the iterator.
enumerate (self) -> Enumerate<Self>
where Self: Sized
Consumes iterator to create an iterator which gives current iteration count and the next value
filter<P> (self, predicate: P) -> Filter<Self, P>
where P: FnMut(&Self::Item) -> bool, Self: Sized
Consumes iterator to create an iterator which takes a closure to determine if an item should be yielded.
for_each<F> (self, f: F)
where F: FnMut(Self::Item), Self: Sized
Calls a closure f on each element of an iterator to make in-place changes of items
map<B, F> (self, f: F) -> Map<Self, F>
where F: FnMut(Self::Item) -> B, Self: Sized
Creates an iterator that calls a closure f on each item
There are more methods that are frequently used in idiomatic Rust code, including: by_ref, eq, find, nth, position, rev, skip, step_by, take. Find documentation here: std::Iter.
/*---------------------------------------------------------
  Demonstrate iterator methods
*/
fn demo_methods() {
  show_label("iterator methods", 25);

  show_op("original vector");
  let mut v = vec![1, 2, 3, 2, 1];
  println!("{v:?}");
  show_op("modified using for_each()");
  /* inplace modification of elements of v */
  v.iter_mut().for_each(|item| *item *= *item);
  println!("{v:?}");

  show_op("collect squared items from vec into array");
  let sq:[i32; 5] =
    /* return iterator over squared items from v */
    v.iter().map(|&item| item * item)
      /* collect invokes iterator to load modified elements into sq */
      .collect::<Vec<i32>>().try_into()
      /* display message if collection panics, e.g., fails and terminates */
      .expect("incorrect length");
  println!("{sq:?}");

  show_op("filter out elements larger than 20");
  let filtered: Vec<i32> =
    /* create iterator over filtered elements */
    sq.iter().filter(|&&item| item <= 20)
    /* copy filtered element and collect into Vec */
    .cloned().collect();
  println!("{filtered:?}");
}


                  
-------------------------
  iterator methods
-------------------------
--- original vector ---
[1, 2, 3, 2, 1]

--- modified using for_each() ---
[1, 4, 9, 4, 1]



--- collect squared items from vec into array ---
[1, 16, 81, 16, 1]








--- filter out elements larger than 20 ---
[1, 16, 16, 1]

2.7 Program Structure

The code structure of this demonstration is very similar to that used in earlier Bits, so is hidden by default.
Code Structure
/*-----------------------------------------------
Bits::rust_iter::main.rs
- demonstrates iteration over collections with
  Rust iterators
- Most collections implement the Rust trait
  IntoIterator which consumes the collection
  to generate an iterator.
- Many also supply functions iter() and mut_iter()
  which return iterators without consuming originial
  collection.
- Demonstrates iteration over arrays, slices,
  Vecs, VecDeques, and custom Point<T, N> type.
-----------------------------------------------*/

#![allow(dead_code)]
#![allow(unused_variables)]

/*-----------------------------------------------
  - Module analysis_iter provides functions
    for type analysis and display.
  - Module points_iter defines type Point<T, N>,
    a point in N-dimensional hyperspace.
*/
use std::collections::*;
mod analysis_iter;
use analysis_iter::*;
mod points_iter;
use points_iter::*;

use std::fmt::*;
use std::cmp::*;

fn demo_loop_iteration() {
  /* code elided */
}
fn demo_for_iteration() {
  /* code elided */
}
fn demo_iter() {
  /* code elided */
}
fn demo_point_iteration() {
  /* code elided */
}
/*-- Begin demonstrations ---------------------*/

fn main() {
  analysis_iter::show_label("Demonstrate Rust Iteration",30);
  
  demo_loop_iteration();
  demo_for_iteration();
  demo_iter();
  demo_point_iteration();

  println!("\nThat's all folks!\n");
}

Program Structure:

This program illustrates how iterators are created and used.
It also shows how custom types can declare and define 
their own iterators.














This demonstration is partitioned into three modules:
- main.rs (this file)
    a sequence of demonstration functions, each focused
    on one type of syntax and the main function that
    controls processing
- points_iter.rs
    defines custome type Point<T, N>
- analysis_iter.rs
    defines functions for type analysis and display






Each of the functions invoked here are illustrated using
blocks with left and right panels, shown above this
block.

Code is shown in a left panel and output in a right panel,
seperated by a splitter-bar enabling views of content that
may be hidden in default display.

3.0 Build


C:\github\JimFawcett\Bits\Rust\rust_iter
> cargo run
   Compiling rust_iter v0.1.0 (C:\github\JimFawcett\Bits\Rust\rust_iter)
    Finished dev [unoptimized + debuginfo] target(s) in 0.50s
C:\github\JimFawcett\Bits\Rust\rust_iter
>

4.0 VS Code View

The code for this demo is available in github.com/JimFawcett/Bits. If you click on the Code dropdown you can clone the repository of all code for these demos to your local drive. Then, it is easy to bring up any example, in any of the languages, in VS Code. Here, we do that for Rust\rust_iter. Figure 1. VS Code IDE - Debug Rust Iter

5.0 References

Reference Description
RustBite_Iterators RustBite on Iterators and Adapters
Rust Story E-book with seven chapters covering most of intermediate Rust
Rust Bites Relatively short feature discussions
std::Iter Library documentation for all of the standard methods. These are provided by any type that implements the Iterator trait.