trainings/Rust_2026/rust-bs-2026-09-14/notebooks/15_iterators.ipynb

2832 lines
70 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Iterators"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Iterator & Iterable\n",
"\n",
"* An iterator is any value that implements `std::iter::Iterator` trait:"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"mod explain {\n",
" trait Iterator {\n",
" type Item;\n",
" fn next(&mut self) -> Option<Self::Item>;\n",
" //... many default methods\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* If there’s a natural way to iterate over some type, that type can implement `std::iter::IntoIterator`, whose `into_iter` method takes a value and returns an iterator over it:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"mod explain {\n",
" trait IntoIterator \n",
" //where Self::IntoIter::Item == Self::Item \n",
" {\n",
" type Item;\n",
" type IntoIter: Iterator;\n",
" \n",
" fn into_iter(self) -> Self::IntoIter;\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* An **iterator** is any type that implements `Iterator`.\n",
"\n",
"* An **iterable** is any type that implements `IntoIterator`: you can get an iterator over it by calling its `into_iter` method. The vector reference `&data` is the iterable in this case.\n",
"\n",
"* An iterator **produces** values.\n",
"\n",
"* The values an iterator produces are **items**.\n",
"\n",
"* The code that receives the items an iterator produces is the **consumer** (the for loop is the consumer)."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## for loop"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fender\n",
"Gibson\n",
"PRS\n",
"Yamaha\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 18,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let data = vec![\"Fender\".to_string(), \"Gibson\".to_string(), \"PRS\".to_string(), \"Yamaha\".to_string()];\n",
"\n",
"for element in data {\n",
" println!(\"{}\", element);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* for loop is a shorthand for:"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Fender\n",
"Gibson\n",
"PRS\n",
"Yamaha\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{\n",
" let data = vec![\"Fender\".to_string(), \"Gibson\".to_string(), \"PRS\".to_string(), \"Yamaha\".to_string()];\n",
"\n",
" let mut iterator = data.into_iter();\n",
" while let Some(element) = iterator.next() {\n",
" println!(\"{}\", element);\n",
" }\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Creating iterators"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### iter() and iter_mut()\n",
"\n",
"* Most collection types and array slices like `&[T]` and `&mut [T]` provide `iter()` and `iter_mut()` methods that return the natural iterators over the type, producing a shared or mutable reference to each item"
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{ \n",
" let data = vec![1, 1, 2, 3, 5, 8, 13];\n",
"\n",
" let mut iterator = data.iter();\n",
" assert_eq!(iterator.next(), Some(&1));\n",
" assert_eq!(iterator.next(), Some(&1));\n",
" assert_eq!(iterator.next(), Some(&2));\n",
" assert_eq!(iterator.next(), Some(&3));\n",
" assert_eq!(iterator.next(), Some(&5));\n",
" assert_eq!(iterator.next(), Some(&8));\n",
" assert_eq!(iterator.next(), Some(&13));\n",
" assert_eq!(iterator.next(), None);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* This iterator’s item type is `&i32`: each call to next produces a reference to the next element, until we reach the end of the vector."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Iteration over &str\n",
"\n",
"* There is no `iter()` method on the `&str` string slice type. Instead, if `s` is a `&str`, then `s.bytes()` returns an iterator that produces each byte of s, whereas `s.chars()` interprets the contents as UTF-8 and produces each Unicode character."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### IntoIterator implementations"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* When a type implements `IntoIterator`, you can call its `into_iter()` method yourself, just as a for loop would:"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"use std::collections::BTreeSet;\n",
"\n",
"let mut guitars = BTreeSet::new();\n",
"guitars.insert(\"Yamaha\".to_string());\n",
"guitars.insert(\"Fender\".to_string());\n",
"guitars.insert(\"Gibson\".to_string());\n",
"\n",
"let mut iterator = guitars.into_iter();\n",
"assert_eq!(iterator.next(), Some(\"Fender\".to_string()));\n",
"assert_eq!(iterator.next(), Some(\"Gibson\".to_string()));\n",
"assert_eq!(iterator.next(), Some(\"Yamaha\".to_string()));\n",
"assert_eq!(iterator.next(), None);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Most collections provide several implementations of `IntoIterator`, for shared references `(&T)`, mutable references `(&mut T)`, and owned values`(T)`:\n",
" * Given a **shared reference to the collection**, `into_iter()` returns an iterator that produces shared references to its items. For example, in the preceding code, `(&favorites).into_iter()` would return an iterator whose `Item` type is `&String`.\n",
" * Given a **mutable reference to the collection**, `into_iter()` returns an iterator that produces mutable references to the items. For example, if vector is some `Vec<String>`, the call `(&mut vector).into_iter()` returns an iterator whose `Item` type is `&mut String`.\n",
" * When passed the **collection by value**, `into_iter()` returns an iterator that takes ownership of the collection and returns items by value; \n",
" * The items’ ownership moves from the collection to the consumer, and the original collection is consumed in the process. For example, the call `guitars.into_iter()` in the preceding code returns an iterator that produces each string by value; the consumer receives ownership of each string. When the iterator is dropped, any elements remaining in the `BTreeSet` are dropped too, and the set’s now-empty husk is disposed of.\n",
"\n",
"* Since a for loop applies `IntoIterator::into_iter()` to its operand, these three implementations are what create the following idioms for iterating over shared or mutable references to a collection, or consuming the collection and taking ownership of its elements:"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Yamaha\n",
"Fender\n",
"Gibson\n",
"Yamaha!\n",
"Fender!\n",
"Gibson!\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"use std::collections::BTreeSet;\n",
"\n",
"let mut guitars = vec![\"Yamaha\".to_string(), \"Fender\".to_string(), \"Gibson\".to_string()];\n",
"\n",
"for guitar in &guitars {\n",
" println!(\"{}\", guitar);\n",
"}\n",
"\n",
"for guitar in &mut guitars {\n",
" *guitar += \"!\";\n",
"}\n",
"\n",
"for guitar in guitars {\n",
" println!(\"{}\", guitar);\n",
"}\n",
"\n"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Not every type provides all three implementations. \n",
" * `HashSet`, `BTreeSet` and `BinaryHeap` don’t implement `IntoIterator` on mutable references, since modifying their elements would probably violate the type’s invariants\n",
" * `HashMap` and `BTreeMap` produce mutable reference to their entries’ values, but only shared references to their keys\n",
" * Slices implement two of the three `IntoIterator` variants; since they don’t own their elements, there is no “by value” case"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### IntoIterator in Generics\n",
"\n",
"* You can use a bound like `T: IntoIterator` to restrict the type variable `T` to types that can be iterated over. \n",
"* Or, you can write `T: IntoIterator<Item=U>` to further require the iteration to produce a particular type `U`"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"fn dump<T, U>(t: T)\n",
" where T: IntoIterator<Item = U>,\n",
" U: std::fmt::Debug \n",
"{\n",
" for u in t {\n",
" println!(\"{:?}\", u);\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\"Yamaha\"\n",
"\"Fender\"\n",
"\"Gibson\"\n"
]
}
],
"source": [
"let mut guitars = vec![\"Yamaha\".to_string(), \"Fender\".to_string(), \"Gibson\".to_string()];\n",
"dump(guitars);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### from_fn\n",
"\n",
"* `std::iter::from_fn` creates an iterator from a closure that returns an `Option<T>`. Each call to the closure produces the next item, and iteration ends when the closure returns `None`."
]
},
{
"cell_type": "code",
"execution_count": 17,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
":dep rand = \"0.7\""
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"use rand::random;\n",
"use std::iter::from_fn;\n",
"\n",
"let lengths: Vec<f64> = from_fn(|| Some((random::<f64>() - random::<f64>()).abs()))\n",
" .take(10)\n",
" .collect();"
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[0.1460489564142653, 0.05699591261484327, 0.31470490145547103, 0.48051853856776183, 0.2768923821261209, 0.34452084700737795, 0.45857317951144794, 0.4249180902549178, 0.6220745268714714, 0.08394659480329847]"
]
},
"execution_count": 19,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"lengths"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"fn fibonacci() -> impl Iterator<Item = usize> {\n",
" let mut state = (0, 1);\n",
" \n",
" from_fn(move || {\n",
" let next = state.0;\n",
" state = (state.1, state.0 + state.1);\n",
" Some(next)\n",
" })\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[0, 1, 1, 2, 3, 5, 8, 13, 21, 34]"
]
},
"execution_count": 21,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"fibonacci().take(10).collect::<Vec<_>>()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### drain methods"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* The `drain()` method removes the specified range from the vector in bulk, returning all removed elements as an iterator. If the iterator is dropped before being fully consumed, it drops the remaining removed elements."
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2, 3, 8, 9, 10]\n",
"[4, 5, 6, 7]\n",
"[1]\n"
]
}
],
"source": [
"let mut data = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n",
"let inner = data.drain(3..7).collect::<Vec<i32>>();\n",
"\n",
"println!(\"{:?}\", data);\n",
"println!(\"{:?}\", inner);\n",
"\n",
"data.drain(1..);\n",
"println!(\"{:?}\", data);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Other iterator sources"
]
},
{
"cell_type": "markdown",
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"source": [
"#### `std::ops::Range` - `1..10`\n",
"\n",
"* Endpoints must be an integer type to be iterable"
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4, 5, 6, 7, 8, 9]"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(1..10).collect::<Vec<i32>>()"
]
},
{
"cell_type": "code",
"execution_count": 24,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 3, 5, 7, 9]"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(1..10).step_by(2).collect::<Vec<i32>>()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `std::ops::RangeFrom` - `1..`\n",
"\n",
"* Unbounded iteration. Start must be an integer. May panic or overflow if the value reaches the limit of the type."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"(1..).take(10).collect::<Vec<i32>>()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `Option<T>` - `Some(5).iter()` or `None.iter()`"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"5\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let opt = Some(5);\n",
"for v in opt {\n",
" println!(\"{}\", v);\n",
"}\n",
"\n",
"let opt: Option<i32> = None;\n",
"for v in opt {\n",
" println!(\"{}\", v);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `Result<T, E>`\n",
"\n",
"* `iter()` and `iter_mut()` methods are available for `Result<T, E>`\n",
"* Returns an iterator over the possibly contained value. The iterator yields one value if the result is `Result::Ok`, otherwise none."
]
},
{
"cell_type": "code",
"execution_count": 33,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"fn load_data(path: &str) -> Result<i32, &str> {\n",
" if path != \"not-found\" {\n",
" Ok(42)\n",
" } else {\n",
" Err(\"File not found\")\n",
" }\n",
"}\n",
"\n",
"let data = load_data(\"found\");\n",
"assert_eq!(data.iter().next(), Some(&42));\n",
"\n",
"let data = load_data(\"not-found\");\n",
"assert_eq!(data.iter().next(), None);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `Vec[T]`, `&[T]`"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* `v.windows(3)` - Returns an iterator over contiguous windows of length 3 within the vector."
]
},
{
"cell_type": "code",
"execution_count": 35,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2, 3]\n",
"[2, 3, 4]\n",
"[3, 4, 5]\n",
"[4, 5, 6]\n",
"[5, 6, 7]\n",
"[6, 7, 8]\n",
"[7, 8, 9]\n",
"[8, 9, 10]\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n",
"\n",
"for wnd in v.windows(3) {\n",
" println!(\"{:?}\", wnd);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* `v.chunks(3)` & `v.chunks_mut(3)` - Return iterators over contiguous chunks of length 3 within the vector, with `chunks_mut` providing mutable references."
]
},
{
"cell_type": "code",
"execution_count": 36,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2, 3]\n",
"[4, 5, 6]\n",
"[7, 8, 9]\n",
"[10]\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 36,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"for chunk in v.chunks(3) {\n",
" println!(\"{:?}\", chunk);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* `v.split(predicate)` & `v.split_mut(predicate)` - Return iterators over subslices separated by elements that match the predicate, with `split_mut` providing mutable references."
]
},
{
"cell_type": "code",
"execution_count": 37,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[1, 2]\n",
"[4, 5]\n",
"[7, 8]\n",
"[10]\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"for slice in v.split(|&n| n % 3 == 0) {\n",
" println!(\"{:?}\", slice);\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[10]\n",
"[7, 8]\n",
"[4, 5]\n",
"[1, 2]\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 39,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"for slice in v.rsplit(|&n| n % 3 == 0) {\n",
" println!(\"{:?}\", slice);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `String`, `&str`"
]
},
{
"cell_type": "code",
"execution_count": 41,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let text = \"Nobody expects the Spanish Inquisition!\\n Amongst our weaponry are such diverse elements as fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms.\".to_string();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* `text.bytes()` - produces bytes of the UTF-8 form"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Bytes(Copied { it: Iter([78, 111, 98, 111, 100, 121, 32, 101, 120, 112, 101, 99, 116, 115, 32, 116, 104, 101, 32, 83, 112, 97, 110, 105, 115, 104, 32, 73, 110, 113, 117, 105, 115, 105, 116, 105, 111, 110, 33, 10, 32, 65, 109, 111, 110, 103, 115, 116, 32, 111, 117, 114, 32, 119, 101, 97, 112, 111, 110, 114, 121, 32, 97, 114, 101, 32, 115, 117, 99, 104, 32, 100, 105, 118, 101, 114, 115, 101, 32, 101, 108, 101, 109, 101, 110, 116, 115, 32, 97, 115, 32, 102, 101, 97, 114, 44, 32, 115, 117, 114, 112, 114, 105, 115, 101, 44, 32, 114, 117, 116, 104, 108, 101, 115, 115, 32, 101, 102, 102, 105, 99, 105, 101, 110, 99, 121, 44, 32, 97, 110, 32, 97, 108, 109, 111, 115, 116, 32, 102, 97, 110, 97, 116, 105, 99, 97, 108, 32, 100, 101, 118, 111, 116, 105, 111, 110, 32, 116, 111, 32, 116, 104, 101, 32, 80, 111, 112, 101, 44, 32, 97, 110, 100, 32, 110, 105, 99, 101, 32, 114, 101, 100, 32, 117, 110, 105, 102, 111, 114, 109, 115, 46]) })"
]
},
"execution_count": 42,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"text.bytes()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* `text.chars()` - produces `char`s of UTF-8 represents"
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"Chars(['N', 'o', 'b', 'o', 'd', 'y', ' ', 'e', 'x', 'p', 'e', 'c', 't', 's', ' ', 't', 'h', 'e', ' ', 'S', 'p', 'a', 'n', 'i', 's', 'h', ' ', 'I', 'n', 'q', 'u', 'i', 's', 'i', 't', 'i', 'o', 'n', '!', '\\n', ' ', 'A', 'm', 'o', 'n', 'g', 's', 't', ' ', 'o', 'u', 'r', ' ', 'w', 'e', 'a', 'p', 'o', 'n', 'r', 'y', ' ', 'a', 'r', 'e', ' ', 's', 'u', 'c', 'h', ' ', 'd', 'i', 'v', 'e', 'r', 's', 'e', ' ', 'e', 'l', 'e', 'm', 'e', 'n', 't', 's', ' ', 'a', 's', ' ', 'f', 'e', 'a', 'r', ',', ' ', 's', 'u', 'r', 'p', 'r', 'i', 's', 'e', ',', ' ', 'r', 'u', 't', 'h', 'l', 'e', 's', 's', ' ', 'e', 'f', 'f', 'i', 'c', 'i', 'e', 'n', 'c', 'y', ',', ' ', 'a', 'n', ' ', 'a', 'l', 'm', 'o', 's', 't', ' ', 'f', 'a', 'n', 'a', 't', 'i', 'c', 'a', 'l', ' ', 'd', 'e', 'v', 'o', 't', 'i', 'o', 'n', ' ', 't', 'o', ' ', 't', 'h', 'e', ' ', 'P', 'o', 'p', 'e', ',', ' ', 'a', 'n', 'd', ' ', 'n', 'i', 'c', 'e', ' ', 'r', 'e', 'd', ' ', 'u', 'n', 'i', 'f', 'o', 'r', 'm', 's', '.'])"
]
},
"execution_count": 43,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"text.chars()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* `text.split_whitespace()` - Returns an iterator over the non-whitespace substrings of the string slice."
]
},
{
"cell_type": "code",
"execution_count": 44,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Nobody\n",
"expects\n",
"the\n",
"Spanish\n",
"Inquisition!\n",
"Amongst\n",
"our\n",
"weaponry\n",
"are\n",
"such\n",
"diverse\n",
"elements\n",
"as\n",
"fear,\n",
"surprise,\n",
"ruthless\n",
"efficiency,\n",
"an\n",
"almost\n",
"fanatical\n",
"devotion\n",
"to\n",
"the\n",
"Pope,\n",
"and\n",
"nice\n",
"red\n",
"uniforms.\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 44,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"for text_chunk in text.split_whitespace() {\n",
" println!(\"{}\", text_chunk);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* `text.lines()` - Returns an iterator over the lines of the string slice."
]
},
{
"cell_type": "code",
"execution_count": 54,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Nobody expects the Spanish Inquisition!\n",
"Amongst our weaponry are such diverse elements as fear, surprise, ruthless efficiency, an almost fanatical devotion to the Pope, and nice red uniforms.\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"for line in text.lines() {\n",
" println!(\"{}\", line.trim());\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* `text.matches` - Returns an iterator over all non-overlapping matches of a pattern in the string slice."
]
},
{
"cell_type": "code",
"execution_count": 56,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"2\n",
"3\n",
"4\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 56,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let text = \"A1B2C3D4\".to_string();\n",
"\n",
"for numeric_chunks in text.matches(char::is_numeric) {\n",
" println!(\"{}\", numeric_chunks);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `HashMap`, `BTreeMap`\n",
"\n",
"* `HashMap` - A hash table-based implementation of a map, providing fast lookups and insertions on average.\n",
"* `BTreeMap` - A tree-based implementation of a map, providing ordered key-value pairs and efficient range queries."
]
},
{
"cell_type": "code",
"execution_count": 45,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let mut dictionary = std::collections::HashMap::new();\n",
"\n",
"dictionary.insert(\"apple\", 3);\n",
"dictionary.insert(\"banana\", 2);\n",
"dictionary.insert(\"cherry\", 5);"
]
},
{
"cell_type": "code",
"execution_count": 46,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[\"apple\", \"banana\", \"cherry\"]"
]
},
"execution_count": 46,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dictionary.keys()"
]
},
{
"cell_type": "code",
"execution_count": 47,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[3, 2, 5]"
]
},
"execution_count": 47,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"dictionary.values()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### `HashSet`, `BTreeSet`\n",
"\n",
"* `HashSet` - A hash table-based implementation of a set, providing fast lookups and insertions on average.\n",
"* `BTreeSet` - A tree-based implementation of a set, providing ordered elements and efficient range queries."
]
},
{
"cell_type": "code",
"execution_count": 48,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"union: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]\n",
"intersection: [5, 6, 7, 8, 9, 10]\n",
"difference: [1, 2, 3, 4]\n",
"symmetric_difference: [1, 2, 3, 4, 11, 12, 13, 14, 15]\n"
]
}
],
"source": [
"use std::collections::BTreeSet;\n",
"\n",
"let set_a = (1..=10).collect::<BTreeSet<i32>>();\n",
"let set_b = (5..=15).collect::<BTreeSet<i32>>();\n",
"\n",
"println!(\"union: {:?}\", set_a.union(&set_b).collect::<Vec<_>>());\n",
"\n",
"println!(\"intersection: {:?}\", set_a.intersection(&set_b).collect::<Vec<_>>());\n",
"\n",
"println!(\"difference: {:?}\", set_a.difference(&set_b).collect::<Vec<_>>());\n",
"\n",
"println!(\"symmetric_difference: {:?}\", set_a.symmetric_difference(&set_b).collect::<Vec<_>>());"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"#### Free functions"
]
},
{
"cell_type": "code",
"execution_count": 49,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let empty_iterable = std::iter::empty::<i32>();\n",
"\n",
"let mut iter = empty_iterable.into_iter();\n",
"\n",
"assert_eq!(iter.next(), None);"
]
},
{
"cell_type": "code",
"execution_count": 50,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let one_element_iterable = std::iter::once(42);\n",
"\n",
"let mut iter = one_element_iterable.into_iter();\n",
"\n",
"assert_eq!(iter.next(), Some(42));\n",
"assert_eq!(iter.next(), None);"
]
},
{
"cell_type": "code",
"execution_count": 51,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"\"*-@*-@*-@*-@*-@*-@*-@*-@*-@\""
]
},
"execution_count": 51,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let pattern = std::iter::repeat(\"*-@\").take(9).collect::<String>();\n",
"pattern"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Iterator Adaptors"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* the `Iterator` trait provides a broad selection of **adapter methods**, or simply adapters, that consume one iterator and build a new one with useful behaviors"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### map & filter\n",
"\n",
"* `map` - Transforms each element of an iterator using a closure.\n",
"* `filter` - Retains only the elements of an iterator that satisfy a predicate."
]
},
{
"cell_type": "code",
"execution_count": 27,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[\"Nobody expects the Spanish Inquisition!\", \"Amongst our weaponry are such diverse elements as\", \"fear,\", \"surprise,\", \"ruthless efficiency,\", \"an almost fanatical devotion to the Pope, and nice red uniforms.\"]"
]
},
"execution_count": 27,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let text = \"Nobody expects the Spanish Inquisition!\\n Amongst our weaponry are such diverse elements as \\n fear, \\n surprise, \\n ruthless efficiency, \\n an almost fanatical devotion to the Pope, and nice red uniforms.\".to_string();\n",
"\n",
"let text_lines = text.lines()\n",
" .map(|s| s.trim().to_owned())\n",
" .collect::<Vec<String>>();\n",
"\n",
"text_lines"
]
},
{
"cell_type": "code",
"execution_count": 28,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[\"fear,\", \"surprise,\"]"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let short_lines = text.lines()\n",
" .map(|s| s.trim().to_owned())\n",
" .filter(|s| s.len() < 20)\n",
" .collect::<Vec<String>>();\n",
"\n",
"short_lines"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"* Adapters are lazy"
]
},
{
"cell_type": "code",
"execution_count": 29,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"[\"one\", \"two\", \"three\", \"four\", \"five\", \"six\", \"seven\", \"eight\", \"nine\", \"ten\"]\n",
" .iter()\n",
" .map(|s| println!(\"{}\", s));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### filter_map\n",
"\n",
"* `filter_map` - Combines the functionality of `filter` and `map`, allowing you to both filter and transform elements of an iterator in a single pass.\n",
"* Closure passed to `filter_map` takes an element and returns an `Option` type, which determines if the element should be included in the output and how it should be transformed."
]
},
{
"cell_type": "code",
"execution_count": 30,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4, 5, 6, 7, 9, 10]"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let input_data = \"1\\n2\\n3\\n4\\n5\\nabc\\n6\\n7\\ndefn8\\n9\\n10\";\n",
"\n",
"let numbers = input_data.lines()\n",
" .filter_map(|line| line.parse::<i32>().ok())\n",
" .collect::<Vec<i32>>();\n",
"\n",
"numbers"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### flat_map\n",
"\n",
"* `flat_map` - Transforms each element of an iterator and flattens the result into a single iterator."
]
},
{
"cell_type": "code",
"execution_count": 55,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[\"Tokyo\", \"Kyoto\", \"São Paulo\", \"Brasília\", \"Nairobi\", \"Mombasa\"]\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 55,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"use std::collections::HashMap;\n",
"\n",
"fn create_major_cities_map() -> std::collections::HashMap<&'static str, Vec<&'static str>> {\n",
" let mut major_cities = std::collections::HashMap::new();\n",
" major_cities.insert(\"Japan\", vec![\"Tokyo\", \"Kyoto\"]);\n",
" major_cities.insert(\"The United States\", vec![\"Portland\", \"Nashville\"]);\n",
" major_cities.insert(\"Brazil\", vec![\"São Paulo\", \"Brasília\"]);\n",
" major_cities.insert(\"Kenya\", vec![\"Nairobi\", \"Mombasa\"]);\n",
" major_cities.insert(\"The Netherlands\", vec![\"Amsterdam\", \"Utrecht\"]);\n",
" major_cities.insert(\"Poland\", vec![\"Warsaw\", \"Kraków\"]);\n",
"\n",
" major_cities\n",
"}\n",
"\n",
"{\n",
" let mut major_cities = create_major_cities_map();\n",
" \n",
" let countries = [\"Japan\", \"Brazil\", \"Kenya\"];\n",
" \n",
" let selected_cities = countries.iter()\n",
" .flat_map(|country| &major_cities[country])\n",
" .collect::<Vec<&&str>>();\n",
"\n",
" println!(\"{:?}\", selected_cities);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### flatten\n",
"\n",
"* `flatten` - Flattens a nested iterator into a single iterator by concatenating all the inner iterators."
]
},
{
"cell_type": "code",
"execution_count": 60,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"[\"Amsterdam\", \"Utrecht\", \"Warsaw\", \"Kraków\", \"Portland\", \"Nashville\", \"Nairobi\", \"Mombasa\", \"São Paulo\", \"Brasília\", \"Tokyo\", \"Kyoto\"]\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 60,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"{\n",
" let mut major_cities = create_major_cities_map();\n",
"\n",
" let all_cities: Vec<&&str> = major_cities\n",
" .values()\n",
" .flatten()\n",
" .collect();\n",
"\n",
" println!(\"{:?}\", all_cities);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### take & take_while\n",
"\n",
"* `take(n)` - Returns an iterator that yields the first `n` elements of the original iterator.\n",
"* `take_while(predicate)` - Returns an iterator that yields elements from the original iterator as long as the predicate is satisfied."
]
},
{
"cell_type": "code",
"execution_count": 61,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[\"To: jimb\", \"From: superego <editor@oreilly.com>\"]"
]
},
"execution_count": 61,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let message = \"To: jimb\\r\\n\\\n",
" From: superego <editor@oreilly.com>\\r\\n\\\n",
" \\r\\n\\\n",
" Did you get any writing done today?\\r\\n\\\n",
" When will you stop wasting time plotting fractals?\\r\\n\";\n",
"\n",
"message.lines().take_while(|l| !l.is_empty()).collect::<Vec<&str>>()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### skip & skip_while\n",
"\n",
"* `skip(n)` - Returns an iterator that skips the first `n` elements of the original iterator.\n",
"* `skip_while(predicate)` - Returns an iterator that skips elements from the original iterator as long as the predicate is satisfied."
]
},
{
"cell_type": "code",
"execution_count": 62,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[\"Did you get any writing done today?\", \"When will you stop wasting time plotting fractals?\"]"
]
},
"execution_count": 62,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let message = \"To: jimb\\r\\n\\\n",
" From: superego <editor@oreilly.com>\\r\\n\\\n",
" \\r\\n\\\n",
" Did you get any writing done today?\\r\\n\\\n",
" When will you stop wasting time plotting fractals?\\r\\n\";\n",
"\n",
"message\n",
" .lines()\n",
" .skip_while(|l| !l.is_empty())\n",
" .skip(1)\n",
" .collect::<Vec<&str>>()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### peekable\n",
"\n",
"* A peekable iterator lets you peek at the next item that will be produced without actually consuming it."
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"use std::iter::Peekable;\n",
"\n",
"fn parse_number<I>(tokens: &mut Peekable<I>) -> i32\n",
" where I: Iterator<Item = char> \n",
"{ \n",
" let mut number = 0;\n",
"\n",
" loop {\n",
" match tokens.peek() {\n",
" Some(c) if c.is_digit(10) => {\n",
" number = number * 10 + c.to_digit(10).unwrap() as i32; \n",
" }\n",
" _ => return number\n",
" }\n",
" tokens.next();\n",
" }\n",
"}\n",
"\n",
"let mut chars = \"423143,353454\".chars().peekable();\n",
"assert_eq!(parse_number(&mut chars), 423143);\n",
"assert_eq!(chars.next(), Some(','));\n",
"assert_eq!(parse_number(&mut chars), 353454);\n",
"assert_eq!(chars.next(), None);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### fuse\n",
"\n",
"* Once an `Iterator` has returned `None`, the trait doesn’t specify how it ought to behave if you call its next method again. Most iterators just return `None` again, but not all. If your code counts on that behavior, you may be in for a surprise.\n",
"* The fuse adapter takes any iterator and produces one that will definitely continue to return `None` once it has done so the first time:"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"struct Flaky(bool);\n",
"\n",
"impl Iterator for Flaky {\n",
" type Item = &'static str;\n",
" fn next(&mut self) -> Option<Self::Item> {\n",
" if self.0 {\n",
" self.0 = false;\n",
" Some(\"totally the last item\")\n",
" } else {\n",
" self.0 = true; // D'oh!\n",
" None\n",
" }\n",
" }\n",
"}\n",
"\n",
"let mut flaky = Flaky(true);\n",
"assert_eq!(flaky.next(), Some(\"totally the last item\"));\n",
"assert_eq!(flaky.next(), None);\n",
"assert_eq!(flaky.next(), Some(\"totally the last item\"));\n",
"\n",
"let mut not_flaky = Flaky(true).fuse();\n",
"assert_eq!(not_flaky.next(), Some(\"totally the last item\"));\n",
"assert_eq!(not_flaky.next(), None);\n",
"assert_eq!(not_flaky.next(), None);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Reversible Iterators & rev\n",
"\n",
"* The `rev` adapter takes any iterator and produces one that produces the same items in reverse order\n",
"* The `DoubleEndedIterator` trait extends `Iterator` with a `next_back` method that returns the last item of the iterator, or `None` if the iterator is empty."
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"mod explain {\n",
" trait DoubleEndedIterator: Iterator {\n",
" fn next_back(&mut self) -> Option<Self::Item>;\n",
" //... many default methods\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"fn main() {\n",
" let mut data = vec![1, 2, 3, 4];\n",
" \n",
" let mut iterator = data.iter().rev();\n",
" assert_eq!(iterator.next(), Some(&4));\n",
" assert_eq!(iterator.next(), Some(&3));\n",
" assert_eq!(iterator.next(), Some(&2));\n",
" assert_eq!(iterator.next(), Some(&1));\n",
" assert_eq!(iterator.next(), None);\n",
"}\n",
"\n",
"main();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### chain\n",
"\n",
"The chain adapter appends one iterator to another. More precisely, `i1.chain(i2)` returns an iterator that draws items from `i1` until it’s exhausted, and then draws items from `i2`.|"
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[1, 2, 3, 4, 5, 6]"
]
},
"execution_count": 67,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let v: Vec<i32> = (1..4).chain(vec![4, 5, 6]).collect();\n",
"v"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"A `chain` iterator is reversible, if both of its underlying iterators are:"
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[6, 5, 4, 3, 2, 1]"
]
},
"execution_count": 68,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let v_reversed: Vec<i32> = (1..4).chain(vec![4, 5, 6]).rev().collect();\n",
"v_reversed"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### enumerate\n",
"\n",
"* `enumerate` - Adds a counter to an iterator and returns it as an iterator of tuples, where each tuple contains the index and the element."
]
},
{
"cell_type": "code",
"execution_count": 69,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"0: zero\n",
"1: one\n",
"2: two\n",
"3: three\n",
"4: four\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 69,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let words = vec![\"zero\", \"one\", \"two\", \"three\", \"four\"];\n",
"\n",
"for (index, item) in words.into_iter().enumerate() {\n",
" println!(\"{}: {}\", index, item);\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"[(0, 'A'), (1, 'B'), (2, 'C'), (3, 'D'), (4, 'E')]"
]
},
"execution_count": 14,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let v: Vec<(i32, char)> = (0..5).zip(\"ABCDE\".chars()).collect();\n",
"v"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### by_ref\n",
"\n",
"An iterator’s by_ref method borrows a mutable reference to the iterator, so that you can apply adaptors to the reference. When you’re done consuming items from these adaptors, you drop them, the borrow ends, and you regain access to your original iterator."
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"To: SuperUser\n",
"From: HelpDesk\n",
"Hello!\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 70,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let message = \"To: SuperUser\\r\\nFrom: HelpDesk\\r\\n\\r\\nHello!\\r\\n\";\n",
"\n",
"let mut lines = message.lines();\n",
"\n",
"for headers in lines.by_ref().take_while(|l| !l.is_empty()) {\n",
" println!(\"{}\", headers);\n",
"}\n",
"\n",
"for body in lines {\n",
" println!(\"{}\", body);\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### cloned\n",
"\n",
"The cloned adapter takes an iterator that produces references, and returns an iterator that produces values cloned from those references. Naturally, the referent type must implement `Clone`"
]
},
{
"cell_type": "code",
"execution_count": 71,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let words = vec![\"zero\".to_string(), \"one\".to_string(), \"two\".to_string(), \"three\".to_string(), \"four\".to_string()];\n",
"\n",
"assert_eq!(words.iter().next(), Some(&\"zero\".to_string()));\n",
"assert_eq!(words.iter().cloned().next(), Some(\"zero\".to_string()));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### cycle\n",
"\n",
"The cycle adapter returns an iterator that endlessly repeats the sequence produced by the underlying iterator. The underlying iterator must implement `std::clone::Clone`, so that cycle can save its initial state and reuse it each time the cycle starts again"
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"North\n",
"East\n",
"South\n",
"West\n",
"North\n",
"East\n",
"South\n",
"West\n",
"North\n",
"East\n",
"South\n",
"West\n"
]
},
{
"data": {
"text/plain": [
"()"
]
},
"execution_count": 72,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let directions = [\"North\", \"East\", \"South\", \"West\"];\n",
"\n",
"for direction in directions.iter().cycle().take(12) {\n",
" println!(\"{}\", direction);\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1\n",
"2\n",
"fizz\n",
"4\n",
"buzz\n",
"fizz\n",
"7\n",
"8\n",
"fizz\n",
"buzz\n",
"11\n",
"fizz\n",
"13\n",
"14\n",
"fizzbuzz\n",
"16\n",
"17\n",
"fizz\n",
"19\n"
]
}
],
"source": [
"use std::iter::{once, repeat};\n",
"\n",
"fn main() {\n",
"let fizzes = repeat(\"\").take(2).chain(once(\"fizz\")).cycle();\n",
"let buzzes = repeat(\"\").take(4).chain(once(\"buzz\")).cycle();\n",
"let fizzes_buzzes = fizzes.zip(buzzes);\n",
"\n",
"let fizz_buzz = (1..20).zip(fizzes_buzzes)\n",
" .map(|tuple|\n",
" match tuple {\n",
" (i, (\"\", \"\")) => i.to_string(),\n",
" (_, (fizz, buzz)) => format!(\"{}{}\", fizz, buzz)\n",
" });\n",
"\n",
"for line in fizz_buzz {\n",
" println!(\"{}\", line);\n",
"}\n",
"}\n",
"\n",
"main();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### count, sum, product\n",
"\n",
"The count method draws items from an iterator until it returns `None`, and tells you how many it got."
]
},
{
"cell_type": "code",
"execution_count": 63,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"49"
]
},
"execution_count": 63,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let evens_count = (1..100).filter(|&x| x % 2 == 0).count();\n",
"evens_count"
]
},
{
"cell_type": "code",
"execution_count": 42,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"fn triangle(n: u64) -> u64 {\n",
" (1..n+1).sum()\n",
"}\n",
"assert_eq!(triangle(20), 210);\n",
"\n",
"fn factorial(n: u64) -> u64 {\n",
" (1..n+1).product()\n",
"}\n",
"assert_eq!(factorial(20), 2432902008176640000);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### max, min\n",
"\n",
"* `max` - Returns the maximum element of an iterator, if it exists.\n",
"* `min` - Returns the minimum element of an iterator, if it exists."
]
},
{
"cell_type": "code",
"execution_count": 43,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"assert_eq!([-2, 0, 1, 0, -2, -5].iter().max(), Some(&1));\n",
"assert_eq!([-2, 0, 1, 0, -2, -5].iter().min(), Some(&-5));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Rust’s floating-point types `f32` and `f64` implement only `std::cmp::PartialOrd`, `not std::cmp::Ord`, so you can’t use the `min` and `max` methods to compute the least or greatest of a sequence of floating-point numbers. This is not a popular aspect of Rust’s design, but it is deliberate: it’s not clear what such functions should do with IEEE NaN values. Simply ignoring them would risk masking more serious problems in the code."
]
},
{
"cell_type": "code",
"execution_count": 64,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"ename": "Error",
"evalue": "the trait bound `{float}: Ord` is not satisfied",
"output_type": "error",
"traceback": [
"\u001b[31m[E0277] Error:\u001b[0m the trait bound `{float}: Ord` is not satisfied",
" \u001b[38;5;246m╭\u001b[0m\u001b[38;5;246m─\u001b[0m\u001b[38;5;246m[\u001b[0m command_64:1:1 \u001b[38;5;246m]\u001b[0m",
" \u001b[38;5;246m│\u001b[0m",
" \u001b[38;5;246m2 │\u001b[0m \u001b[38;5;249ml\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m \u001b[0m\u001b[38;5;249m=\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249ma\u001b[0m\u001b[38;5;249ml\u001b[0m\u001b[38;5;249m_\u001b[0m\u001b[38;5;249mn\u001b[0m\u001b[38;5;249mu\u001b[0m\u001b[38;5;249mm\u001b[0m\u001b[38;5;249mb\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249ms\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;249mi\u001b[0m\u001b[38;5;249mt\u001b[0m\u001b[38;5;249me\u001b[0m\u001b[38;5;249mr\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m.\u001b[0m\u001b[38;5;54mm\u001b[0m\u001b[38;5;54mi\u001b[0m\u001b[38;5;54mn\u001b[0m\u001b[38;5;249m(\u001b[0m\u001b[38;5;249m)\u001b[0m\u001b[38;5;249m;\u001b[0m",
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m─\u001b[0m\u001b[38;5;54m┬\u001b[0m\u001b[38;5;54m─\u001b[0m ",
" \u001b[38;5;240m │\u001b[0m \u001b[38;5;54m╰\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m\u001b[38;5;54m─\u001b[0m the trait `Ord` is not implemented for `{float}`",
"\u001b[38;5;246m───╯\u001b[0m"
]
}
],
"source": [
"let real_numbers = vec![1.3, 2.4, 3.5, 4.6, 5.7];\n",
"let min_number =real_numbers.iter().min();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### min_by, max_by\n",
"\n",
"The `max_by` and `min_by` methods return the maximum or minimum item the iterator produces, as determined by a comparison function you provide:"
]
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n",
"thread '<unnamed>' panicked at src\\lib.rs:26:26:\n",
"called `Option::unwrap()` on a `None` value\n",
"stack backtrace:\n",
" 0: std::panicking::begin_panic_handler\n",
" at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\std\\src\\panicking.rs:697\n",
" 1: core::panicking::panic_fmt\n",
" at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:75\n",
" 2: core::panicking::panic\n",
" at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\panicking.rs:145\n",
" 3: core::option::unwrap_failed\n",
" at /rustc/1159e78c4747b02ef996e55082b704c09b970588/library\\core\\src\\option.rs:2130\n",
" 4: std::panic::catch_unwind\n",
" 5: run_user_code_61\n",
" 6: <unknown>\n",
" 7: <unknown>\n",
" 8: <unknown>\n",
" 9: <unknown>\n",
" 10: <unknown>\n",
" 11: <unknown>\n",
" 12: <unknown>\n",
" 13: <unknown>\n",
" 14: BaseThreadInitThunk\n",
" 15: RtlUserThreadStart\n",
"note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.\n"
]
}
],
"source": [
"use std::cmp::{PartialOrd, Ordering};\n",
"\n",
"// Compare two f64 values. Panic if given a NaN.\n",
"fn cmp(lhs: &&f64, rhs: &&f64) -> Ordering {\n",
" lhs.partial_cmp(rhs).unwrap()\n",
"}\n",
"\n",
"let numbers = [1.0, 4.0, 2.0];\n",
"assert_eq!(numbers.iter().max_by(cmp), Some(&4.0));\n",
"assert_eq!(numbers.iter().min_by(cmp), Some(&1.0));\n",
"\n",
"let numbers = [1.0, 4.0, std::f64::NAN, 2.0];\n",
"assert_eq!(numbers.iter().max_by(cmp), Some(&4.0)); // panics"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### min_by_key, max_by_key\n",
"\n",
"The `max_by_key` and `min_by_key` methods are similar to `max_by` and `min_by`, but they take a key extraction function that computes the value to compare:"
]
},
{
"cell_type": "code",
"execution_count": 66,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"use std::collections::HashMap;\n",
"\n",
"let mut populations = HashMap::new();\n",
"populations.insert(\"Portland\", 583_776);\n",
"populations.insert(\"Fossil\", 449);\n",
"populations.insert(\"Greenhorn\", 2);\n",
"populations.insert(\"Boring\", 7_762);\n",
"populations.insert(\"The Dalles\", 15_340);\n",
"\n",
"assert_eq!(populations.iter().max_by_key(|&(_name, pop)| pop),\n",
" Some((&\"Portland\", &583_776)));\n",
"assert_eq!(populations.iter().min_by_key(|&(_name, pop)| pop),\n",
" Some((&\"Greenhorn\", &2)));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Comparing Item Sequences\n",
"\n",
"You can use `<` and `==` operators to compare strings, vectors and slices. However, you can’t compare iterators directly.\n",
"Iterators provide functions `eq` and `lt` that do comparisons."
]
},
{
"cell_type": "code",
"execution_count": 67,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let packed = \"John Doe,123 Main St.,Hometown,CA,12345\";\n",
"let spaced = \"John Doe, 123 Main St., Hometown, CA, 12345\";\n",
"\n",
"assert!(packed.split(',').eq(spaced.split(',').map(str::trim)));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### any, all"
]
},
{
"cell_type": "code",
"execution_count": 68,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let sentence = \"The quick brown fox jumps over the lazy dog\";\n",
"\n",
"assert!(sentence.chars().any(char::is_uppercase));\n",
"assert!(sentence.chars().all(|c| c.is_ascii_alphabetic() || c.is_whitespace()));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### position, rposition\n",
"\n",
"The position method applies a closure to each item from the iterator and returns the index of the first item for which the closure returns `true`. More precisely, it returns an `Option` of the index: if the closure returns `true` for no item, position returns `None`. It stops drawing items as soon as the closure returns `true`."
]
},
{
"cell_type": "code",
"execution_count": 70,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let text = \"The quick brown fox jumps over the lazy dog\";\n",
"assert_eq!(text.chars().position(|c| c.is_whitespace()), Some(3));\n",
"\n",
"let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 11];\n",
"assert_eq!(numbers.iter().rposition(|&n| n % 2 == 0), Some(7));"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### fold\n",
"\n",
"The fold method is a very general tool for accumulating some sort of result over the entire sequence of items an iterator produces. Given an initial value, which we’ll call the accumulator, and a closure, fold repeatedly applies the closure to the current accumulator and the next item from the iterator. The value the closure returns is taken as the new accumulator, to be passed to the closure with the next item. The final accumulator value is what fold itself returns. If the sequence is empty, fold simply returns the initial accumulator."
]
},
{
"cell_type": "code",
"execution_count": 72,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n",
"\n",
"assert_eq!(v.iter().fold(0, |acc, _| acc + 1), 10); // count\n",
"assert_eq!(v.iter().fold(0, |acc, n| acc + n), 55); // sum\n",
"assert_eq!(v.iter().fold(1, |acc, n| acc * n), 3_628_800); // product"
]
},
{
"cell_type": "code",
"execution_count": 73,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"data": {
"text/plain": [
"\"And now for something completely different\""
]
},
"execution_count": 73,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"let words = [\"And\", \"now\", \"for\", \"something\", \"completely\", \"different\"];\n",
"\n",
"let sentence = words.iter().fold(String::new(), |mut acc, &word| {\n",
" acc.push_str(word);\n",
" acc.push(' ');\n",
" acc\n",
"});\n",
"\n",
"sentence.trim()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### nth\n",
"\n",
"The `nth` method takes an index `n`, skips that many items from the iterator, and returns the next item, or `None` if the sequence ends before that point. Calling `.nth(0)` is equivalent to `.next()`.\n",
"\n",
"It doesn’t take ownership of the iterator the way an adapter would, so you can call it many times."
]
},
{
"cell_type": "code",
"execution_count": 75,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"fn main() {\n",
" let mut squares = (0..10).map(|n| n * n);\n",
"\n",
" assert_eq!(squares.nth(4), Some(16));\n",
" assert_eq!(squares.nth(0), Some(25));\n",
" assert_eq!(squares.nth(3), Some(81));\n",
" assert_eq!(squares.nth(2), None);\n",
"}\n",
"\n",
"main();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### last\n",
"\n",
"The last method consumes items until the iterator returns `None`, and then returns the last item. If the iterator produces no items, then last returns `None`"
]
},
{
"cell_type": "code",
"execution_count": 76,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"fn main() {\n",
" let squares = (0..10).map(|n| n * n);\n",
" assert_eq!(squares.last(), Some(81));\n",
"}"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"This consumes all the iterator’s items starting from the front, even if the iterator is reversible. If you have a reversible iterator and don’t need to consume all its items, you should instead just write `iter.rev().next()`."
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### find\n",
"\n",
"The find method draws items from an iterator, returning the first item for which the given closure returns true, or None if the sequence ends before a suitable item is found"
]
},
{
"cell_type": "code",
"execution_count": 77,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Some(529)\n"
]
}
],
"source": [
"fn main() {\n",
" let mut squares = (0..100).map(|n| n * n);\n",
"\n",
" let first_gt_500 = squares.find(|&n| n > 500);\n",
" println!(\"{:?}\", first_gt_500);\n",
"}\n",
"\n",
"main();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### partition\n",
"\n",
"The partition method divides an iterator’s items among two collections, using a closure to decide where each item belongs:"
]
},
{
"cell_type": "code",
"execution_count": 39,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"even numbers: [2, 4, 6, 8, 10]\n",
"odd numbers: [1, 3, 5, 7, 9]\n"
]
}
],
"source": [
"fn main() {\n",
" let numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];\n",
"\n",
" let (evens, odds): (Vec<i32>, Vec<i32>) = numbers.iter().partition(|&n| n % 2 == 0);\n",
"\n",
" println!(\"even numbers: {:?}\", evens);\n",
" println!(\"odd numbers: {:?}\", odds);\n",
"}\n",
"\n",
"main();"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Implementing an Iterator"
]
},
{
"cell_type": "code",
"execution_count": 74,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"struct I32Range {\n",
" start: i32,\n",
" end: i32,\n",
"}\n",
"\n",
"impl Iterator for I32Range {\n",
" type Item = i32;\n",
"\n",
" fn next(&mut self) -> Option<Self::Item> {\n",
" if self.start < self.end {\n",
" let result = Some(self.start);\n",
" self.start += 1;\n",
" result\n",
" } else {\n",
" None\n",
" }\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 75,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"let mut pi = 0.0;\n",
"let mut numerator = 1.0;\n",
"\n",
"for k in (I32Range { start: 0, end: 14 }) {\n",
" pi += numerator / (2*k + 1) as f64;\n",
" numerator /= -3.0;\n",
"}\n",
"pi *= f64::sqrt(12.0);\n",
"\n",
"// IEEE 754 specifies this result exactly.\n",
"assert_eq!(pi as f32, std::f32::consts::PI);"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Case Study - BinaryTree"
]
},
{
"cell_type": "code",
"execution_count": 77,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"enum BinaryTree<T> {\n",
" Empty,\n",
" NonEmpty(Box<TreeNode<T>>),\n",
"}\n",
"\n",
"struct TreeNode<T> {\n",
" element: T,\n",
" left: BinaryTree<T>,\n",
" right: BinaryTree<T>,\n",
"}\n",
"\n",
"struct TreeIter<'a, T> {\n",
" unvisited: Vec<&'a TreeNode<T>>,\n",
"}\n",
"\n",
"impl<'a, T> TreeIter<'a, T> {\n",
" fn push_left_edge(&mut self, mut tree: &'a BinaryTree<T>) {\n",
" while let BinaryTree::NonEmpty(ref node) = *tree {\n",
" self.unvisited.push(node);\n",
" tree = &node.left;\n",
" }\n",
" }\n",
"}\n",
"\n",
"impl<T> BinaryTree<T> {\n",
" fn iter(&self) -> TreeIter<T> {\n",
" let mut iter = TreeIter { unvisited: Vec::new() };\n",
" iter.push_left_edge(self);\n",
" iter\n",
" }\n",
"}\n",
"\n",
"impl<'a, T: 'a> IntoIterator for &'a BinaryTree<T> {\n",
" type Item = &'a T;\n",
" type IntoIter = TreeIter<'a, T>;\n",
"\n",
" fn into_iter(self) -> Self::IntoIter {\n",
" self.iter()\n",
" }\n",
"}\n",
"\n",
"impl<'a, T> Iterator for TreeIter<'a, T> {\n",
" type Item = &'a T;\n",
"\n",
" fn next(&mut self) -> Option<Self::Item> {\n",
" let node = match self.unvisited.pop() {\n",
" None => return None,\n",
" Some(n) => n,\n",
" };\n",
"\n",
" self.push_left_edge(&node.right);\n",
" Some(&node.element)\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": 78,
"metadata": {
"vscode": {
"languageId": "rust"
}
},
"outputs": [],
"source": [
"use self::BinaryTree::*;\n",
"\n",
"fn make_node<T>(left: BinaryTree<T>, element: T, right: BinaryTree<T>) -> BinaryTree<T> {\n",
" NonEmpty(Box::new(TreeNode { left, element, right }))\n",
"}\n",
"\n",
"fn main() {\n",
" // Build a small tree.\n",
" let subtree_l = make_node(Empty, \"mecha\", Empty);\n",
" let subtree_rl = make_node(Empty, \"droid\", Empty);\n",
" let subtree_r = make_node(subtree_rl, \"robot\", Empty);\n",
" let tree = make_node(subtree_l, \"Jaeger\", subtree_r);\n",
"\n",
" // Iterate over it.\n",
" let mut v = Vec::new();\n",
" for kind in &tree {\n",
" v.push(*kind);\n",
" }\n",
" assert_eq!(v, [\"mecha\", \"Jaeger\", \"droid\", \"robot\"]);\n",
"}\n",
"\n",
"main();"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Rust",
"language": "rust",
"name": "rust"
},
"language_info": {
"codemirror_mode": "rust",
"file_extension": ".rs",
"mimetype": "text/rust",
"name": "Rust",
"pygment_lexer": "rust",
"version": ""
}
},
"nbformat": 4,
"nbformat_minor": 2
}