mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 04:54:17 -06:00
finished ch13.2
This commit is contained in:
parent
f921ed2326
commit
ef22125714
@ -11,7 +11,7 @@ It will discuss some features of Rust that are similar to features in many langu
|
||||
|
||||
It will cover:
|
||||
- [*Closures*](./Closures.md) - a function-like construct you can store in a variable
|
||||
- *Iterators* - a way of processing a series of elements
|
||||
- [*Iterators*](./Iterators.md) - a way of processing a series of elements
|
||||
- How to use colsure and iterators to improve the I/O project (minigrep)
|
||||
- The preformance of closures and iterators (Spoiler alert: they are faster than you might think!)
|
||||
|
275
Iterators.md
Normal file
275
Iterators.md
Normal file
@ -0,0 +1,275 @@
|
||||
# Processing a Series of Items with Iterators
|
||||
Iterator patterns allow you to perform some task on a sequence of items in turn.
|
||||
|
||||
An iterator is responsibl for the logic of iterating over each item and determining when the sequence has finished.
|
||||
|
||||
When you use iterators, you don't have to reimplement that logic yourself again.
|
||||
|
||||
Rust's iterators are *lasy*, meaning they have no effect until yo call methods that consume the iterator to use it up
|
||||
|
||||
In this example the code creates an iterator over the items in the vecotr `v1` by calling the `iter` method defined on `Vec<T>`
|
||||
|
||||
This code does nothing useful.
|
||||
```rust
|
||||
let v1 = vec![1, 2, 3];
|
||||
|
||||
let v1_iter = v1.iter();
|
||||
```
|
||||
The iterator is stored in the `v1_iter` variable.
|
||||
|
||||
Once an iterator is created, we can use it in a variety of ways.
|
||||
|
||||
Before we iterator over an array using a `for` loop to execute some code on each of its items.
|
||||
|
||||
Under the hood this implicitly created and then consumed an iterator, but we glossed over how exactly that works until now.
|
||||
|
||||
In this example, we separate the creation of the iterator from the use of the iterator in the `for` loop.
|
||||
|
||||
When the `for` loop is called using th iterator in `v1_iter`, each element in the iterator is used in one iteration of the loop which prints out each value.
|
||||
```rust
|
||||
let v1 = vec![1, 2, 3];
|
||||
|
||||
let v1_iter = v1.iter();
|
||||
|
||||
for val in v1_iter {
|
||||
println!("Got: {val}");
|
||||
}
|
||||
```
|
||||
In languages that don't have iterators provided by their std libraries, you would likely wirte this same functionality by starting at index 0.
|
||||
|
||||
Using that variable to index into th vector to get a value and incrememting the variable in a loop until it reached the total number of items in the vector.
|
||||
|
||||
Iterators handle all that logic for you, removing repetitive code that you could mess up.
|
||||
|
||||
Iterators gives mroe flexibility to use the same logic with many different kinds of sequences, not just data structs you can index into (vectors for example).
|
||||
|
||||
## The `Iterator` Trait and the `next` Method
|
||||
All iterators implement a trait named `Iterator` that is defined in the std library
|
||||
|
||||
The defintion of the `Iterator` trait looks like this:
|
||||
```rust
|
||||
pub trait Iterator {
|
||||
type Item;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item>;
|
||||
|
||||
// methods with default implementations elided
|
||||
}
|
||||
```
|
||||
|
||||
Note this definition uses some new syntax, `type Item` and `Self::Item`, which are defining an *associated type* with this trait
|
||||
|
||||
Associated types will be discussed in ch20 (Advanced Features)
|
||||
|
||||
For now know that this code says implementing the `Iterator` trait requires you also defined an `Item` type.
|
||||
|
||||
This `Item` type is usd in the return in the return type of the `next` method.
|
||||
|
||||
The `Item` tpye ill be the type returned form the iterator.
|
||||
|
||||
The `Iterator` trait only requires implmentors to define one method; `next` method which returns one item of the iterator at a time wrapped in `Some` and when the iteration is over, returns `None`.
|
||||
|
||||
We can call the `next` method on iterators directly
|
||||
|
||||
This example demonstrates what values are returned from repeated calls to `next` on the iterator created from the vector.
|
||||
```rust
|
||||
#[test]
|
||||
fn iterator_demonstration() {
|
||||
let v1 = vec![1, 2, 3];
|
||||
|
||||
let mut v1_iter = v1.iter();
|
||||
|
||||
assert_eq!(v1_iter.next(), Some(&1));
|
||||
assert_eq!(v1_iter.next(), Some(&2));
|
||||
assert_eq!(v1_iter.next(), Some(&3));
|
||||
assert_eq!(v1_iter.next(), None);
|
||||
}
|
||||
```
|
||||
|
||||
Notice that we need to make `v1_iter` mutable.
|
||||
|
||||
Calling the `next` method on an iterator changes internal state that the iterator uses to keep track of where it is in the sequence.
|
||||
|
||||
It could also be said that the code *consumes*, or uses up, the iterator.
|
||||
|
||||
Each call to `next` comsumes an item form the iterator.
|
||||
|
||||
We didn't need to make `v1_iter` mutable when we used a `for` loop because the loop took ownership of `v1_iter` and made it mutable under the hood.
|
||||
|
||||
Note as well the values that we get from the `next` are immutable reference to the values in the vector.
|
||||
|
||||
The `iter` mthod produces an iterator over immutable references.
|
||||
|
||||
If we want to create an iterator that takes ownership of `v1` and returns owned values, we can call `into_iter`.
|
||||
|
||||
If you want to iterate over mutalbe references, you can call `iter_mut`
|
||||
|
||||
## Methods that Consume the Iterator
|
||||
The `Iterator` trait has a number of different methods with default implementations provided by the std library.
|
||||
|
||||
You can find out about these by looking the std library API documentation for the `Iterator` trait.
|
||||
|
||||
Some these methods call the `next` method in their definition, which is why you are required to implement the `Iterator` trait.
|
||||
|
||||
Methods that call `next` are called *consuming adapters*, becaise calling them uses up the iterator.
|
||||
|
||||
One example is the `sum` metod which takes ownership of the iterator and iterates through the items by repeatedly calling `next` thus consuming the iterator.
|
||||
|
||||
As it iterates through it adds each item to a running total, this comsumes the iterator.
|
||||
|
||||
When it is complete it return the total
|
||||
|
||||
Here is a test illustrating a use of the `sum` method
|
||||
```rust
|
||||
#[test]
|
||||
fn iterator_sum() {
|
||||
let v1 = vec![1, 2, 3];
|
||||
|
||||
let v1_iter = v1.iter();
|
||||
|
||||
let total: i32 = v1_iter.sum();
|
||||
|
||||
assert_eq!(total, 6);
|
||||
}
|
||||
```
|
||||
We aren't allowed to use `v1_iter` after the call to `sum because it takes ownership of the iterator we call it on.
|
||||
|
||||
## Methods that Produce Other Iterators
|
||||
*Iterator adapters* are methods defined on the `Iterator` trait that don't consume the iterator.
|
||||
|
||||
Instead, they produce different iterators by changing some aspect of the original iterator.
|
||||
|
||||
This example shows calling an iterator adapter method `map`, which takes a closure to call on each item as the items are iterated through.
|
||||
|
||||
The `map` method returns a new iterator that produces the modified items.
|
||||
|
||||
The closure here creates a new iterator in which each item from the vector will be incremeted by 1
|
||||
```rust
|
||||
let v1: Vec<i32> = vec![1, 2, 3];
|
||||
|
||||
v1.iter().map(|x| x + 1);
|
||||
```
|
||||
But this code produces a warning
|
||||
```
|
||||
$ cargo run
|
||||
Compiling iterators v0.1.0 (file:///projects/iterators)
|
||||
warning: unused `Map` that must be used
|
||||
--> src/main.rs:4:5
|
||||
|
|
||||
4 | v1.iter().map(|x| x + 1);
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
|
||||
= note: iterators are lazy and do nothing unless consumed
|
||||
= note: `#[warn(unused_must_use)]` on by default
|
||||
help: use `let _ = ...` to ignore the resulting value
|
||||
|
|
||||
4 | let _ = v1.iter().map(|x| x + 1);
|
||||
| +++++++
|
||||
|
||||
warning: `iterators` (bin "iterators") generated 1 warning
|
||||
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.47s
|
||||
Running `target/debug/iterators`
|
||||
```
|
||||
|
||||
This doesnt do anything. The closure we sepcified never gets called. The warning reminds us why: iterator adapters are lazy and we need to consume the iterator here.
|
||||
|
||||
To fix this warning and consume the iterator, we will use the `collect` method, which we used in Ch 12 with `env::args`.
|
||||
|
||||
This method consumes the iterator and collects the resulting values into a collection data type.
|
||||
|
||||
In this next example we collect the results of the iterating over the iterator that is returned form the call to `map` into a vector.
|
||||
|
||||
This vector will end up containing each item form the original vector incremeted by 1.
|
||||
```rust
|
||||
let v1: Vec<i32> = vec![1, 2, 3];
|
||||
|
||||
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
|
||||
|
||||
assert_eq!(v2, vec![2, 3, 4]);
|
||||
```
|
||||
|
||||
Because `map` takes a closure, we can specify any operation we want to perform on each item.
|
||||
|
||||
This is a good eample of how closures let you do custome behavior while reusing the iteration behavior that the `Iterator` trait provides.
|
||||
|
||||
You can chain mutliple calls to iterator adapters to perform compex action is a readable way.
|
||||
|
||||
Due to all iterators being lasy, you have to call one of the consuming adapter methods to get results ffrom calls to iterator adapters.
|
||||
|
||||
## Using Closures that Capture Their Environment
|
||||
Many iterator adapters take closures as args and commonly the closures we will specify as ags to iterator adapters will be closures that capture their environment.
|
||||
|
||||
In this example we use the `filter` method that takes a closure.
|
||||
|
||||
The closure get an item form the iterator and returns a `bool`
|
||||
|
||||
If the closure returns `true`, the value will be included in the iteration produced by `filter`.
|
||||
|
||||
If the closure returns `false`, the value won't be included.
|
||||
|
||||
Here we use `filer` with a closure that captures the `shoe_size` variable from its environment to iterate over a collection of `Shoe` struct instances.
|
||||
|
||||
It will return only shoes that are the specified size.
|
||||
```rust
|
||||
#[derive(PartialEq, Debug)]
|
||||
struct Shoe {
|
||||
size: u32,
|
||||
style: String,
|
||||
}
|
||||
|
||||
fn shoes_in_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
|
||||
shoes.into_iter().filter(|s| s.size == shoe_size).collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn filters_by_size() {
|
||||
let shoes = vec![
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("sneaker"),
|
||||
},
|
||||
Shoe {
|
||||
size: 13,
|
||||
style: String::from("sandal"),
|
||||
},
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("boot"),
|
||||
},
|
||||
];
|
||||
|
||||
let in_my_size = shoes_in_size(shoes, 10);
|
||||
|
||||
assert_eq!(
|
||||
in_my_size,
|
||||
vec![
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("sneaker")
|
||||
},
|
||||
Shoe {
|
||||
size: 10,
|
||||
style: String::from("boot")
|
||||
},
|
||||
]
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
The `shoes_in_size` function takes ownership of a vector of shoes and a shoe size parameters.
|
||||
|
||||
It returns a vector containing only shoes of the specified size.
|
||||
|
||||
In the body of `shoes_in_size` we call `into_iter` to create an iterator that takes ownership of the vector.
|
||||
|
||||
We then call `filter` to adapt that iterator into a new iterator that only contains elements for which the closure returns `true`.
|
||||
|
||||
The closure captures the `shoe_size` parameter from the environment and compares the value with each shoe's size, keeping only shoes of the size specified.
|
||||
|
||||
Calling `collect` finally gathers the values returned by the adapted iterator into a vector that is returned by the function.
|
||||
|
||||
This test shows that when we call `shoes_in_size`, we get back only shoes that have the same size as te value specified.
|
7
adder/Cargo.lock
generated
Normal file
7
adder/Cargo.lock
generated
Normal file
@ -0,0 +1,7 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adder"
|
||||
version = "0.1.0"
|
@ -1 +1 @@
|
||||
{"rustc_fingerprint":13662911779824945272,"outputs":{"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.83.0 (90b35a623 2024-11-26)\nbinary: rustc\ncommit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf\ncommit-date: 2024-11-26\nhost: x86_64-pc-windows-msvc\nrelease: 1.83.0\nLLVM version: 19.1.1\n","stderr":""},"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\Brock\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"lahfsahf\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"pc\"\nub_checks\nwindows\n","stderr":""},"12744816824612481171":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\Brock\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""}},"successes":{}}
|
||||
{"rustc_fingerprint":4305361489769004817,"outputs":{"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.0 (4d91de4e4 2025-02-17)\nbinary: rustc\ncommit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688\ncommit-date: 2025-02-17\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.0\nLLVM version: 19.1.7\n","stderr":""}},"successes":{}}
|
@ -1 +1 @@
|
||||
{"rustc_fingerprint":13662911779824945272,"outputs":{"15729799797837862367":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\Brock\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\nfmt_debug=\"full\"\noverflow_checks\npanic=\"unwind\"\nproc_macro\nrelocation_model=\"pic\"\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"lahfsahf\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_has_atomic_equal_alignment=\"128\"\ntarget_has_atomic_equal_alignment=\"16\"\ntarget_has_atomic_equal_alignment=\"32\"\ntarget_has_atomic_equal_alignment=\"64\"\ntarget_has_atomic_equal_alignment=\"8\"\ntarget_has_atomic_equal_alignment=\"ptr\"\ntarget_has_atomic_load_store\ntarget_has_atomic_load_store=\"128\"\ntarget_has_atomic_load_store=\"16\"\ntarget_has_atomic_load_store=\"32\"\ntarget_has_atomic_load_store=\"64\"\ntarget_has_atomic_load_store=\"8\"\ntarget_has_atomic_load_store=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_thread_local\ntarget_vendor=\"pc\"\nub_checks\nwindows\n","stderr":""},"4614504638168534921":{"success":true,"status":"","code":0,"stdout":"rustc 1.83.0 (90b35a623 2024-11-26)\nbinary: rustc\ncommit-hash: 90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf\ncommit-date: 2024-11-26\nhost: x86_64-pc-windows-msvc\nrelease: 1.83.0\nLLVM version: 19.1.1\n","stderr":""},"12744816824612481171":{"success":true,"status":"","code":0,"stdout":"___.exe\nlib___.rlib\n___.dll\n___.dll\n___.lib\n___.dll\nC:\\Users\\Brock\\.rustup\\toolchains\\stable-x86_64-pc-windows-msvc\npacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"msvc\"\ntarget_family=\"windows\"\ntarget_feature=\"cmpxchg16b\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_feature=\"sse3\"\ntarget_has_atomic=\"128\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"windows\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"pc\"\nwindows\n","stderr":""}},"successes":{}}
|
||||
{"rustc_fingerprint":4305361489769004817,"outputs":{"13331785392996375709":{"success":true,"status":"","code":0,"stdout":"___\nlib___.rlib\nlib___.so\nlib___.so\nlib___.a\nlib___.so\n/home/brock/.rustup/toolchains/stable-x86_64-unknown-linux-gnu\noff\npacked\nunpacked\n___\ndebug_assertions\npanic=\"unwind\"\nproc_macro\ntarget_abi=\"\"\ntarget_arch=\"x86_64\"\ntarget_endian=\"little\"\ntarget_env=\"gnu\"\ntarget_family=\"unix\"\ntarget_feature=\"fxsr\"\ntarget_feature=\"sse\"\ntarget_feature=\"sse2\"\ntarget_has_atomic=\"16\"\ntarget_has_atomic=\"32\"\ntarget_has_atomic=\"64\"\ntarget_has_atomic=\"8\"\ntarget_has_atomic=\"ptr\"\ntarget_os=\"linux\"\ntarget_pointer_width=\"64\"\ntarget_vendor=\"unknown\"\nunix\n","stderr":""},"17747080675513052775":{"success":true,"status":"","code":0,"stdout":"rustc 1.85.0 (4d91de4e4 2025-02-17)\nbinary: rustc\ncommit-hash: 4d91de4e48198da2e33413efdcd9cd2cc0c46688\ncommit-date: 2025-02-17\nhost: x86_64-unknown-linux-gnu\nrelease: 1.85.0\nLLVM version: 19.1.7\n","stderr":""}},"successes":{}}
|
Loading…
x
Reference in New Issue
Block a user