mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 04:54:17 -06:00
started ch13.1 with files
All checks were successful
Test Gitea Actions / first (push) Successful in 13s
All checks were successful
Test Gitea Actions / first (push) Successful in 13s
This commit is contained in:
parent
1ba56701cf
commit
f2e65b3550
128
Closures.md
Normal file
128
Closures.md
Normal 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
20
Iteratos and Closures.md
Normal 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.
|
Loading…
x
Reference in New Issue
Block a user