about
Bits_Iter Rust
06/03/2023
Bits Repo Code Bits Repo Docs

Bits_Iter Rust

code, output, and build for Rust on Windows, macOS, and Linux

This page is a prototype used to decide which language features to emphasize. It will be replaced with a final version soon.

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 CodeSnaps

Source Code - main.rs

/*-----------------------------------------------
  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.
  - Demonstrates iteration over arrays, slices,
    Vecs, VecDeques, and custom PointN<T> type.
-----------------------------------------------*/

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

use std::collections::*;
mod analysis_iter;
// use analysis_iter::*;
mod points_iter;
use points_iter::*;

/*---------------------------------------------------------
  Use of Rust iterators is encapsulated in a sequence of
  functions defined below and used in main.  This file
  builds collections and applies the various functions
  to illustrate how iterators are used.

  It starts with a few quick iteration examples before
  applying the analysis functions.
---------------------------------------------------------*/
/*
- vec_indexer<T:Debug>(v:&Vec<T>)
- slice_indexer<T:Debug>(s:&[T])
- sub_range_indexer<T:Debug>(
    s:&[T],
    mut lower:usize, mut upper:usize
  )
- slice_looper<T:Debug>(s:&[T])
- collection_looper<C: Debug, I: Debug>(c:&C)
    where C: IntoIterator<Item = I> + Clone
- for_looper<C: Debug, I: Debug>(c:&C)
    where C: IntoIterator<Item = I> + Clone
- ranger<T>(iter: &mut T)
    where T: Iterator, T::Item: Debug
*/
use std::fmt::*;
use std::cmp::*;

/*-----------------------------------------------
  vec_indexer<T: Debug>(v:&Vec<T>)
-------------------------------------------------
Simplest case - displays Vector with generic
Item type.
- uses naive indexing
- works only for Vec's
*/
pub fn vec_indexer<T: Debug>(v:&Vec<T>) {
  let mut i = 0;
  while i < v.len() {
    print!("{:?} ", v[i]);
    i += 1;
  }
  println!();
}
/*-----------------------------------------------
  slice_indexer<T:Debug>(s:&[T])
-------------------------------------------------
Illustrates indexing in slices
- Not idiomatic, but safe and correct.
- Works with any collection with contiguous
  fixed size elements.
- Won't work with String or str inputs; they
  are collections of utf8 chars which vary
  in size from 1 to 4 bytes.
- It is rare for idomatic Rust code to use
  indexing.
- &[T] is slice type
- Demo's in main show how to use for various
  types.
*/
#[allow(clippy::needless_range_loop)]
pub fn slice_indexer<T:Debug>(s:&[T]) {
  let max = s.len();
  /* 0..max is range iterator */
  for i in 0..max {
      print!("{:?} ", s[i]);
  }
  println!();
  /*---------------------------
    clippy prefers no indexing:
    for item in s.iter().take(max) {
      print!("{item} ");
    }
  */
}
/*-----------------------------------------------
  sub_range_indexer<T:Debug>(
    s:&[T], mut lower:usize, mut upper:usize
  )
Iterates over a sub-range of the slice s
- works with any collection with contiguous
  fixed size elements.
*/
#[allow(clippy::needless_range_loop)]
pub fn sub_range_indexer<T:Debug>(
  s:&[T], mut lower:usize, mut upper:usize
) {
  lower = max(0, lower);
  upper = min(s.len(), upper);
  if lower <= upper {
    for i in lower..upper {
      print!("{:?} ", s[i]);
    }
  }
  println!();
}
/*-----------------------------------------------
  slice_looper<T:Debug>(s:&[T])
-------------------------------------------------
Iterates over slice s without indexing
- Works with any collection with contiguous
  fixed size elements,
  e.g., array, Vector, PointN, ...
- Uses slice iterator.
*/
pub fn slice_looper<T:Debug>(s:&[T]) {
  let mut iter = s.iter();
  loop {
      let item = iter.next();
      match item {
          Some(val) => print!("{val:?} "),
          None => break
      }
  }
  println!();
}
/*-----------------------------------------------
  collection_looper<C:Debug, I:Debug>(c:&C)
-------------------------------------------------
- prints comma separated list of Collection<I>'s
  items.
- where clause requires C to implement
  IntoIterator trait.
- C is type of collection, I is type of C's items
- Accepts any collection type that implements
  IntoIterator trait, e.g., array, slice, Vector, ...
- collection_looper can't accept String or &str
  because String does not implement IntoIterator
- That's because String provides two iterators,
  chars() to iterate multibyte chars and bytes()
  to iterate over bytes.
- Not very efficient - uses three order N operations,
  clone, collect, and loop.
  https://stackoverflow.com/questions/49962611/why-does-str-not-implement-intoiterator
*/
pub fn collection_looper<C: Debug, I: Debug>(c:&C)
where C: IntoIterator<Item = I> + Clone
{
  let cc = c.clone();
  let iter = cc.into_iter();
  /* convert c into Vec to get len() method */
  let v:Vec<_> = iter.collect();
  let mut iter = v.iter();  // shadowing
  let mut count = 0;
  loop {
      let item = iter.next();
      match item {
          Some(val) => print!("{val:?}"),
          None => { println!(); break; }
      }
      if count < v.len() - 1 {
          print!(", ");
      }
      count += 1;
  }
}
/*-----------------------------------------------
  for_looper<C: Debug, I: Debug>(c:&C)
-------------------------------------------------
- prints comma separated list of Collection<I>'s
  items.
- similar to collection_looper but erases last comma
  so no need for collection or clone
- uses idiomatic forloop with no indexing
*/
pub fn for_looper<C: Debug, I: Debug>(c:&C)
  where C: IntoIterator<Item = I> + Clone
{
  /* build string of comma separated values */
  let mut accum = String::new();
  let cc = c.clone();
  for item in cc {  // converts cc into iterator
      accum += &format!("{item:?}, ");
  }
  /* remove last comma */
  let opt = find_last_utf8(&accum, ',');
  if let Some(index) = opt {
      accum.truncate(index);
  }
  println!("{accum}");
}
/*-- find last char in str --*/
pub fn find_last_utf8(s:&str, chr: char) -> Option<usize> {
  s.chars().rev().position(|c| c == chr)
   .map(|rev_pos| s.chars().count() - rev_pos -1)
}
/*-----------------------------------------------
  ranger<T>(iter: &mut T)
-------------------------------------------------
- Displays contents of iterator, often passed in
  as range.
- another idiomatic iteration.
*/
pub fn ranger<T>(iter: &mut T)
  where T: Iterator, T::Item: Debug
{
  for item in iter {
      print!("{item:?} ")
  }
  println!();
}
/*-----------------------------------------------
  demo_adapters<C, I>(c: C, i: I) -> Vec<I>
-------------------------------------------------
- iterates over collection C,
  removes non-positive items, adds second
  argument i and collects into vector.
-------------------------------------------------
- adapters accept an iterator and return a
  revised iterator, as discussed below.
- adapter filter builds iterator over elements
  that satisfy a predicate defined by closure
- map builds iterator that modifies elements
  according to a closure.
- Adapter collect runs iterator and collects
  into Vec<I>.
*/
pub fn demo_adapters<C, I>(c: C, i: I) -> Vec<I>
where
  C: IntoIterator<Item = I> + Debug + Clone,
  I: std::ops::Add<Output = I> + std::ops::Mul<Output = I>
      + PartialOrd + PartialEq + Debug + Default + Copy,
{
  let def = I::default();  // expect value is zero
  c.into_iter()
      .filter(|item| item > &def)
      .map(|item| item + i)
      .collect()
}

/*-- Begin demonstrations ---------------------*/

fn main() {
  analysis_iter::show_label("Demonstrate Rust Iteration",30);

  let s = &mut [1usize, 2, 3, 4, 3, 2, 1];
  println!("slice s = {s:?}");
  println!("s[2] = {:?}", s[2usize]);

  /*-- PointN<T>.into_iter() -----------------*/
  let mut p = PointN::<i32>::new(5);
  p[1] = 1;
  p[3] = -1;
  println!("\n{p:?}");
  println!("using PointN<i32>.iter");
  for item in p.iter() {
      print!("{item:?} ");
  }
  println!("\nusing PointN<i32>.into_iter");
  let iter = p.clone().into_iter(); // consumes clone
  analysis_iter::show_op("displaying iter type");
  analysis_iter::show_type(&iter, "iter");
  for item in iter {
      print!("{item} ");
  }
  println!();
  println!("using PointN<i32>.into_iter iter() with auto deref");
  let pc = p.clone();
  for item in pc {  // auto deref of pc into pc.iter()
      print!("{item} " ) // consumes pc
  }
  println!();
  println!("using PointN<i32>.iter()");
  for item in p.iter() { // does not consume p
      print!("{item} " )
  }
  println!();
  println!("using PointN<i32>.iter_mut()");
  for item in p.iter_mut() { // does not consume p
      *item *= 2;
      print!("{item} " )
  }
  println!("\n{p:?}");

  /*-- vec_indexer -------------------------------*/
  println!("\nvec_indexer displays Vec<T>");
  let v = vec![1, 6, 2, 5, 3, 4];
  vec_indexer(&v);  // can only display vecs
  println!();

  /*-- slice_indexer -----------------------*/
  println!("slice_indexer displays slice");
  slice_indexer(s);
  println!("slice_indexer displays vector");
  let v = vec![5, 4, 3, 2, 1, 0];
  slice_indexer(&v);
  println!("slice_indexer displays string bytes");
  let str1:&str = "a string";
  slice_indexer(str1.as_bytes());
  println!();

  /*-- sub_range_indexer --------------------*/
  println!("sub_range_indexer displays slice");
  sub_range_indexer(s, 2, 5);
  println!();

  /*-- slice_looper ------------------------*/
  println!("slice_looper displays slice");
  slice_looper(s);
  println!("slice_looper displays vector");
  slice_looper(&v);
  println!("slice_looper displays PointN");
  let mut point = PointN::<i32>::new(5);
  point[1] = 2;
  point[3] = -3;
  let ps = &point[0..];  // take slice
  slice_looper(ps);
  println!();

  /*-- collection_looper -------------------------------*/
  println!("collection_looper displays slice:");
  collection_looper(s);
  println!("collection_looper displays array");
  let a = [1, 2, 3];
  collection_looper(&a);
  println!("collection_looper displays VecDeque");
  let vecdeq = VecDeque::from([4, 3, 2, 0, -1]);
  collection_looper(&vecdeq);
  println!("collection_looper displays PointN");
  let pc = point.clone();
  collection_looper(&pc);
  println!("{pc:?}");
  println!();

  /*-- for_looper ---------------------------*/
  println!("for_looper displays slice:");
  for_looper(s);
  println!("for_looper displays vector:");
  let vec = s.to_vec();
  for_looper(&vec);
  println!("for_looper displays VecDeque");
  for_looper(&vecdeq);
  println!("for_looper displays PointN<T>");
  let pc = point.clone();
  for_looper(&pc);
  println!();
  /*-------------------------------------------
    for_looper can't accept String or &str
    because they do not implement IntoIterator
    https://stackoverflow.com/questions/49962611/why-does-str-not-implement-intoiterator
  */

  /*-- ranger -------------------------------*/
  println!("ranger displays string:");
  let str = "a literal string".to_string();
  ranger(&mut str.chars());
  println!("ranger displays values in range");
  ranger(&mut (0..10));
  println!("ranger accepts Vector iterator");
  ranger(&mut vec.iter());
  println!("ranger accepts VecDeque iterator");
  ranger(&mut vecdeq.iter());
  println!("ranger accepts PointN<T> iterator");
  ranger(&mut point.iter());
  println!();

  /*-- demo_adapters ------------------------*/
  println!("demo_adapters<T, i32>(coll, 2) accepts array:");
  let a = [1, -1, 0, 2, 3, 4];
  println!("{:?} ", &a);
  let vo = demo_adapters(a, 2);
  println!("{:?} ", &vo);

  println!("demo_adapters<T, f64>(coll, 1.5) accepts PointN<f64>:");
  let mut pad = PointN::<f64>::new(5);
  pad[0] = 1.5;
  pad[1] = -2.0;
  pad[2] = 0.0;
  pad[3] = 1.1;
  pad[4] = 2.2;
  // this assignment works only in local module
  // pad.items = vec![1.5, -2.0, 0.0, 1.1, 2.2];
  println!("{:?} ", &pad);
  let vo = demo_adapters(&pad, 1.5);
  println!("{:?} ", &vo);

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

Source Code - points_iter.rs

/*-- PointN<T> -----------------------------------
  PointN<T> declares a PointN type holding a
  Vec<T> of coordinate values.
  It implements:
  - new(n)  constructor
  - iter()  returns iterator over items
  - trait IntoIterator for PointN<T>
  - trait IntoIterator for &PointN<T>
  - 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 PointN<T>
    where T:Debug + Default + Clone
{
  items: Vec<T>
}
impl<T> PointN<T>
    where T:Debug + Default + Clone
{
  pub fn new(n:usize) -> PointN<T> {
    PointN::<T> {
      items: vec![T::default(); n],
    }
  }
  pub fn is_empty(&self) -> bool {
    self.items.is_empty()
  }
  pub fn len(&self) -> usize {
    self.items.len()
  }
  pub fn push(&mut self, item:T) {
    self.items.push(item);
  }
  pub fn pop(&mut self) -> Option<T> {
    self.items.pop()
  }
  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:Debug, Idx> std::ops::Index<Idx> for PointN<T>
    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, Idx> std::ops::IndexMut<Idx> for PointN<T>
    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<'a, T> IntoIterator for &'a PointN<T>
    where T:Debug + Default + Clone
{
  type Item = T;
  type IntoIter = std::vec::IntoIter<Self::Item>;
  fn into_iter(self) -> Self::IntoIter {
    let ccln = self.items.clone();
    ccln.into_iter()
  }
}
/*-- IntoIterator trait for PointN<T> ----------*/
impl<T> IntoIterator for PointN<T>
    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()
  }
}
          

Source Code - analysis_iter.rs

/*-----------------------------------------------
  analysis_iter.cs:
    Test functions with increasing functionality
    and increasing generality of inputs:
-----------------------------------------------*/

use std::fmt::*;

/*---------------------------------------------------------
  Show input's call name and type
  - doesn't consume input
  - show_type is generic function with Debug bound.
    Using format "{:?}" requires Debug.
*/
pub fn show_type<T:Debug>(_t: &T, nm: &str) {
  let typename = std::any::type_name::<T>();
  println!("{nm:?}, type: {typename:?}");
}
/*---------------------------------------------------------
  Show enumerable input's values
  - 'a is an annotation saying that T's lifetime
    is as long as the function's lifetime.
  - I is the type of T's elements, that is coll:T<I>.
  - T can be any iterable type and both T and I must
    satisfy Debug trait.
  - Does not consume input t since passed by reference.
*/
pub fn show_value_enum<T:Debug, I:Debug>(
  t: &T, nm: &str, left:usize, width:usize
)
  where for<'a> &'a T: IntoIterator<Item = &'a I>
{
  println!("{nm:?} {{");
  show_fold(t, left, width);
  print!("}}");
  println!("\nsize: {}", std::mem::size_of::<T>());
}
/*---------------------------------------------------------
  Show facts about a type's elements, e.g., name, type,
  value, and size.
  - show_type is generic function with Debug bound.
    Using format "{:?} requires Debug."
  - works with small enumerable collections too because
    {:?} knows how to format them, but won't fold long
    sequences of elements. Use show_value_enum for that.
*/
pub fn show_type_scalar<T:Debug>(t: &T, nm: &str) {
  show_type(t, nm);
  println!(
    "value: {t:?}, size: {}", std::mem::size_of::<T>()
  );
}
/*---------------------------------------------------------
Show facts about an enumerable type's elements, e.g.,
name, type, values, and size.
- show_type is generic function with Debug bound.
  Using format "{:?} requires Debug."
*/
pub fn show_type_enum<T:Debug, I:Debug>(t: &T, nm: &str, left:usize, width:usize)
  where for<'a> &'a T: IntoIterator<Item = &'a I>
{
  show_type(t, nm);
  show_value_enum(t, nm, left, width);
}
/*---------------------------------------------------------
  build indent string with "left" spaces
*/
pub fn offset(left: usize) -> String {
  let mut accum = String::new();
  for _i in 0..left {
    accum += " ";
  }
  accum
}
/*---------------------------------------------------------
  find index of last occurance of chr in s
  - returns option in case chr is not found
  https://stackoverflow.com/questions/50101842/how-to-find-the-last-occurrence-of-a-char-in-a-string
*/
fn find_last_utf8(s: &str, chr: char) -> Option<usize> {
  s.chars().rev().position(|c| c== chr)
    .map(|rev_pos| s.chars().count() - rev_pos - 1)
  /*-- alternate implementation --*/
  // if let Some(rev_pos) =
  //   s.chars().rev().position(|c| c == chr) {
  //     Some(s.chars().count() - rev_pos - 1)
  // } else {
  //     None
  // }
}
/*---------------------------------------------------------
  fold an enumerable's elements into rows of w elements
  - indent by left spaces
  - does not consume t since passed as reference
  - returns string
  https://users.rust-lang.org/t/generic-code-over-iterators/10907/3
*/
pub fn fold<T, I:Debug>(
  t: &T, left: usize, width: usize
) -> String
    where for<'a> &'a T: IntoIterator<Item = &'a I>, T:Debug
{
  let mut accum = String::new();
  accum += &offset(left);

  for (i, item) in t.into_iter().enumerate() {
    accum += &format!("{item:?}, ");
    if ((i + 1) % width) == 0 && i != 0 {
        accum += "\n";
        accum += &offset(left);
    }
  }
  let opt = find_last_utf8(&accum, ',');
  if let Some(index) = opt {
    accum.truncate(index);
  }
  accum
  /*-- Alternate direct implementation --*/
  //let mut i = 0usize;
  // for item in t {
  //   accum += &format!("{item:?}, ");
  //   if ((i + 1) % width) == 0 && i != 0 {
  //       accum += "\n";
  //       accum += &offset(left);
  //   }
  //   i += 1;
  // }
}
/*---------------------------------------------------------
  show enumerables's elements as folded rows
  - width is number of elements in each row
  - left is indent from terminal left
*/
pub fn show_fold<T:Debug, I:Debug>(t:&T, left:usize, width:usize)
  where for<'a> &'a T: IntoIterator<Item = &'a I>
{
  println!("{}",fold(t, left, width));
}
/*---------------------------------------------------------
  show string wrapped with long dotted lines above and below
*/
pub fn show_label(note: &str, n:usize) {
  let mut line = String::new();
  for _i in 0..n {
    line.push('-');
  }
  print!("\n{line}\n");
  print!("  {note}");
  print!("\n{line}\n");
}
pub fn show_label_def(note:&str) {
  show_label(note, 50);
}
/*---------------------------------------------------------
  show string wrapped with dotted lines above and below
*/
pub fn show_note(note: &str) {
  print!("\n-------------------------\n");
  print!(" {note}");
  print!("\n-------------------------\n");
}
/*---------------------------------------------------------
  show string wrapped in short lines
*/
pub fn show_op(opt: &str) {
  println!("--- {opt} ---");
}
/*---------------------------------------------------------
  print newline
*/
pub fn nl() {
  println!();
}
          

Output

C:\github\JimFawcett\Bits\Rust\rust_iter
> cargo run -q

------------------------------
  Demonstrate Rust Iteration
------------------------------
slice s = [1, 2, 3, 4, 3, 2, 1]
s[2] = 3

PointN { items: [0, 1, 0, -1, 0] }
using PointN<i32>.iter
0 1 0 -1 0
using PointN<i32>.into_iter
--- displaying iter type ---
"iter", type: "alloc::vec::into_iter::IntoIter<i32>"
0 1 0 -1 0
using PointN<i32>.into_iter iter() with auto deref
0 1 0 -1 0
using PointN<i32>.iter()
0 1 0 -1 0
using PointN<i32>.iter_mut()
0 2 0 -2 0
PointN { items: [0, 2, 0, -2, 0] }

vec_indexer displays Vec<T>
1 6 2 5 3 4

slice_indexer displays slice
1 2 3 4 3 2 1
slice_indexer displays vector
5 4 3 2 1 0
slice_indexer displays string bytes
97 32 115 116 114 105 110 103

sub_range_indexer displays slice
3 4 3

slice_looper displays slice
1 2 3 4 3 2 1
slice_looper displays vector
5 4 3 2 1 0
slice_looper displays PointN
0 2 0 -3 0

collection_looper displays slice:
1, 2, 3, 4, 3, 2, 1
collection_looper displays array
1, 2, 3
collection_looper displays VecDeque
4, 3, 2, 0, -1
collection_looper displays PointN
0, 2, 0, -3, 0
PointN { items: [0, 2, 0, -3, 0] }

for_looper displays slice:
1, 2, 3, 4, 3, 2, 1
for_looper displays vector:
1, 2, 3, 4, 3, 2, 1
for_looper displays VecDeque
4, 3, 2, 0, -1
for_looper displays PointN<T>
0, 2, 0, -3, 0

ranger displays string:
'a' ' ' 'l' 'i' 't' 'e' 'r' 'a' 'l' ' ' 's' 't' 'r' 'i' 'n' 'g'
ranger displays values in range
0 1 2 3 4 5 6 7 8 9
ranger accepts Vector iterator
1 2 3 4 3 2 1
ranger accepts VecDeque iterator
4 3 2 0 -1
ranger accepts PointN<T> iterator
0 2 0 -3 0

demo_adapters<T, i32>(coll, 2) accepts array:
[1, -1, 0, 2, 3, 4]
[3, 4, 5, 6]
demo_adapters<T, f64>(coll, 1.5) accepts PointN<f64>:
PointN { items: [1.5, -2.0, 0.0, 1.1, 2.2] }
[3.0, 2.6, 3.7]

That's all folks!

C:\github\JimFawcett\Bits\Rust\rust_iter
>          

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
>
          

2.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

3.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
  bottom refs VS Code bld out anal pts src codeSnaps top
  Next Prev Pages Sections About Keys