From ef22125714fac6a8780dbf07968f529db3a88c06 Mon Sep 17 00:00:00 2001 From: darkicewolf50 Date: Mon, 24 Feb 2025 13:11:28 -0700 Subject: [PATCH] finished ch13.2 --- ...d Closures.md => Iterators and Closures.md | 2 +- Iterators.md | 275 ++++++++++++++++++ adder/Cargo.lock | 7 + guessing_game/target/.rustc_info.json | 2 +- hello_cargo/target/.rustc_info.json | 2 +- 5 files changed, 285 insertions(+), 3 deletions(-) rename Iteratos and Closures.md => Iterators and Closures.md (93%) create mode 100644 Iterators.md create mode 100644 adder/Cargo.lock diff --git a/Iteratos and Closures.md b/Iterators and Closures.md similarity index 93% rename from Iteratos and Closures.md rename to Iterators and Closures.md index e5dbce9..0650f1c 100644 --- a/Iteratos and Closures.md +++ b/Iterators and Closures.md @@ -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!) diff --git a/Iterators.md b/Iterators.md new file mode 100644 index 0000000..5be559b --- /dev/null +++ b/Iterators.md @@ -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` + +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; + + // 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 = 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 = 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_size: u32) -> Vec { + 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. \ No newline at end of file diff --git a/adder/Cargo.lock b/adder/Cargo.lock new file mode 100644 index 0000000..efe4e14 --- /dev/null +++ b/adder/Cargo.lock @@ -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" diff --git a/guessing_game/target/.rustc_info.json b/guessing_game/target/.rustc_info.json index 95f8a64..64a38fc 100644 --- a/guessing_game/target/.rustc_info.json +++ b/guessing_game/target/.rustc_info.json @@ -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":{}} \ No newline at end of file +{"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":{}} \ No newline at end of file diff --git a/hello_cargo/target/.rustc_info.json b/hello_cargo/target/.rustc_info.json index b287c23..64a38fc 100644 --- a/hello_cargo/target/.rustc_info.json +++ b/hello_cargo/target/.rustc_info.json @@ -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":{}} \ No newline at end of file +{"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":{}} \ No newline at end of file