mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 21:14:18 -06:00
All checks were successful
Test Gitea Actions / first (push) Successful in 13s
128 lines
5.2 KiB
Markdown
128 lines
5.2 KiB
Markdown
# 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 |