|
Creates an iterator provided by C and consumes c. Elements of |
|
|
Returns an iterator without consuming Elements of |
|
|
Returns an iterator without consuming Elements of |
/*---------------------------------------------------------
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
|
/* do something with item */ } |
|
|
/* do something with item */ } |
Since reference |
|
/* do something that mutates item */ } |
Since |
/*---------------------------------------------------------
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]
/*---------------------------------------------------------
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
/*-- 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()
}
}
/*---------------------------------------------------------
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] }
| 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 |
/*---------------------------------------------------------
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]
/*-----------------------------------------------
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 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 custom typePoint<T, N> - analysis_iter.rs defines functions for type analysis and display Blocks with left and right panels illustrate each function invoked here, shown above this block. The left panel shows code and the right panel shows output, separated by a splitter-bar that reveals content hidden in the default display.
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
>
| 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. |