Compare commits

...

2 Commits

Author SHA1 Message Date
f2e65b3550 started ch13.1 with files
All checks were successful
Test Gitea Actions / first (push) Successful in 13s
2025-02-19 23:51:24 +00:00
1ba56701cf started ch13.1 and created a cleaned up version of mingrep 2025-02-19 23:50:36 +00:00
4 changed files with 263 additions and 0 deletions

128
Closures.md Normal file
View File

@ -0,0 +1,128 @@
# Closures: Anonymous Functions that Capture Their Environment
Rust closures are anonymous functions you can save in a variable or pass as an argument ot other functions
You create the closure in one plcae and then call the closure elsewhere to evaluate it in a different context.
Unlike functions, closures can capture values form the scope in which they are defined
This will be demonstrated how thee closure features allow for code reuse and behavior customization
## Capturing the Environment with Closures
We will first examine how we can use closures to capture values fomr the environment they are defined in for later use
Here is the scenario that we will use to examine this:
Every so often, our t-shirt company gives away an exclusive, limited-edition shirt to someone on our mailing list as a promotion.
People on the mailing list can optionally add their colour to their profile.
If the persion cohsen for a free shirt has their favourite colour set, they get that colour shirt.
If the persion hasn't specified a favourite colour, they get whatever colour the company currently gas the most of.
There are many ways to implement this.
For example we are going to use an enum called `ShirtColor` that has the varaiants `Red` and `Blue`(we limit the number of colours avaialable for simplicity)
We represent the company's inventory with an `Inventory` struct that has a field named `shirts` that contains a `Vec<ShirtColor>` reperenting the shirt colours currently in stock.
The method `giveaway` defined on `Inventory` gets the optional shirt colour preference of the free shirt winner, and reutnrs the shirt colour the person will get.
Here is the setup
```rust
#[derive(Debug, PartialEq, Copy, Clone)]
enum ShirtColor {
Red,
Blue,
}
struct Inventory {
shirts: Vec<ShirtColor>,
}
impl Inventory {
fn giveaway(&self, user_preference: Option<ShirtColor>) -> ShirtColor {
user_preference.unwrap_or_else(|| self.most_stocked())
}
fn most_stocked(&self) -> ShirtColor {
let mut num_red = 0;
let mut num_blue = 0;
for color in &self.shirts {
match color {
ShirtColor::Red => num_red += 1,
ShirtColor::Blue => num_blue += 1,
}
}
if num_red > num_blue {
ShirtColor::Red
} else {
ShirtColor::Blue
}
}
}
fn main() {
let store = Inventory {
shirts: vec![ShirtColor::Blue, ShirtColor::Red, ShirtColor::Blue],
};
let user_pref1 = Some(ShirtColor::Red);
let giveaway1 = store.giveaway(user_pref1);
println!(
"The user with preference {:?} gets {:?}",
user_pref1, giveaway1
);
let user_pref2 = None;
let giveaway2 = store.giveaway(user_pref2);
println!(
"The user with preference {:?} gets {:?}",
user_pref2, giveaway2
);
}
```
The `store` defined in `main` has two blue shirts and one red shirt remaining to distribute for this limited-edition promotion
We call the `giveaway` method for a user with a preference for a red shirt and a user without any preference.
Once again this code could b implemented in man ways and here, to focus on closures, we are stuck to concepts you already learned except for the body of the `giveaway` method that uses a closure.
In the `giveaway` method, we get the user preference as a paramter of type `Option<ShirtColor>` and call the `unwrap_or_else` method on `user_preference`
The [`unwrap_or_else` method on `Option<T>`](https://doc.rust-lang.org/std/option/enum.Option.html#method.unwrap_or_else) is defined by the std library.
It takes one argument: a closure without any args that returns a value `T` (the same tpe stored in the `Some` variant of the `Option<T>`, in this case `ShirtColor`)
If the `Option<T>` is the `Some` variant `unwrap_or_else` returns the value form within the `Some`.
If the `Option<T>` is the `None` varaint, `unwrap_or_else` calls the closure and returns the value returned by the cloosure.
We specify the closure expression `|| self.most_stocked()` as the argument to `unwrap_or_else`.
This is a closure that takes no paramters itself (if the closure has paramters, they would appear between the two verticalbars).
The body of thee closure calls `self.most_stocked()`
We are defining the closure here and the implementation of `unwrap_or_else` will evaluate the closure later if the result is needed
Here is the output of this code
```
$ cargo run
Compiling shirt-company v0.1.0 (file:///projects/shirt-company)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.27s
Running `target/debug/shirt-company`
The user with preference Some(Red) gets Red
The user with preference None gets Blue
```
One aspect to notice is here is that we have passed a closure that calls `self.most_stocked()` on current `Inventory` intance.
The std library didn't need to know anything about te `Inventory` or `ShirtColor` types we defined or the logic we want to use in this scenario.
The closure captures an immutalbe reference to the `self` `Inventory` instance and passes ot woth the code we specify to the `unwrap_or_else` method
Funcions on the other hand are not able to capture their environment in this way.
## Closure Type Inference and Annoation

20
Iteratos and Closures.md Normal file
View File

@ -0,0 +1,20 @@
# Functional Language Features: Iterators and Closures
Rust's design has taken inspiration from many exisitng languages and techniques
One significant influence is *functional programming*
Programming in a functional style often include using functions as values by passing them in args, returning them form other unfcitons, assigning them to varaibles for later execution and so forth.
This chapter/section won't debate the issue of what functional programming is or isn't.
It will discuss some features of Rust that are similar to features in many languages often referred to as functional
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
- 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!)
We have already covered some other Rust freatures, such as pattern matchin and enums, that are also influenced by the functional style.
Mastering closures and iterators is an improtant part of wiritng idiomatic, fast Rust code.

97
minigrep/cleanedUp/lib.rs Normal file
View File

@ -0,0 +1,97 @@
use std::fs;
use std::error::Error;
use std::env;
pub struct Config {
pub query: String,
pub file_path: String,
pub ignore_case: bool,
}
impl Config {
pub fn build(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments");
}
let query = args[1].clone();
let file_path = args[2].clone();
let ignore_case = env::var("IGNORE_CASE").is_ok();
Ok(Config { query, file_path, ignore_case })
}
}
pub fn run(config: Config) -> Result<(), Box<dyn Error>> {
let contents = fs::read_to_string(config.file_path)?;
let results = if config.ignore_case {
search_case_insensitive(&config.query, &contents)
} else {
search(&config.query, &contents)
};
for line in results {
println!("{line}");
}
Ok(())
}
pub fn search<'a>(query: &str, contents: &'a str) -> Vec<&'a str> {
let mut results = Vec::new();
for line in contents.lines() {
if line.contains(query) {
results.push(line);
}
}
results
}
pub fn search_case_insensitive<'a>(
query: &str,
contents: &'a str
) -> Vec<&'a str> {
let query = query.to_lowercase();
let mut results = Vec::new();
for line in contents.lines() {
if line.to_lowercase().contains(&query) {
results.push(line);
}
}
results
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn case_sensitive() {
let query = "duct";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Duct tape.";
assert_eq!(vec!["safe, fast, productive."], search(query, contents));
}
#[test]
fn case_insensitive() {
let query = "rUsT";
let contents = "\
Rust:
safe, fast, productive.
Pick three.
Trust me.";
assert_eq!(
vec!["Rust:", "Trust me."], search_case_insensitive(query, contents)
);
}
}

View File

@ -0,0 +1,18 @@
use std::env;
use std::process;
use minigrep::Config;
fn main() {
let args: Vec<String> = env::args().collect();
let config = Config::build(&args).unwrap_or_else(|err| {
eprintln!("Problem parsing arguments: {err}");
process::exit(1);
});
if let Err(e) = minigrep::run(config) {
eprintln!("Application error: {e}");
process::exit(1);
}
}