Add [Closure] chapter

This commit is contained in:
sunface 2022-03-30 22:29:07 +08:00
parent 47983d35f0
commit 8220143688
5 changed files with 569 additions and 53 deletions

View File

@ -1,5 +1,335 @@
# Closure
Closures can capture the enclosing evironments. For example we can capture the `x` variable :
```rust
fn main() {
let x = 1;
let closure = |val| val + x;
assert_eq!(closure(2), 3);
}
```
From the syntax, we can see that closures are very convenient for on the fly usage. Unlike functions, both the input and return types of a closure can be inferred by the compiler.
```rust
fn main() {
// Increment via closures and functions.
fn function(i: i32) -> i32 { i + 1 }
// Closures are anonymous, here we are binding them to references
//
// These nameless functions are assigned to appropriately named variables.
let closure_annotated = |i: i32| -> i32 { i + 1 };
let closure_inferred = |i | i + 1 ;
let i = 1;
// Call the function and closures.
println!("function: {}", function(i));
println!("closure_annotated: {}", closure_annotated(i));
println!("closure_inferred: {}", closure_inferred(i));
// A closure taking no arguments which returns an `i32`.
// The return type is inferred.
let one = || 1;
println!("closure returning one: {}", one());
}
```
## Capturing
Closures can capture variables by borrowing or moving. But they prefer to capture by borrowing and only go lower when required:
- by reference: `&T`
- by mutable reference: `&mut T`
- by value: `T`
1、🌟
```rust,editable
/* Make it work with least changing */
fn main() {
let color = String::from("green");
let print = move || println!("`color`: {}", color);
print();
print();
// `color` can be borrowed immutably again, because the closure only holds
// an immutable reference to `color`.
let _reborrow = &color;
println!("{}",color);
}
```
2、🌟🌟
```rust,editable
/* Make it work
- Dont use `_reborrow` and `_count_reborrowed`
- Dont modify `assert_eq`
*/
fn main() {
let mut count = 0;
let mut inc = || {
count += 1;
println!("`count`: {}", count);
};
inc();
let _reborrow = &count;
inc();
// The closure no longer needs to borrow `&mut count`. Therefore, it is
// possible to reborrow without an error
let _count_reborrowed = &mut count;
assert_eq!(count, 0);
}
```
3、🌟🌟
```rust,editable
/* Make it work in two ways, none of them is to remove `take(movable)` away from the code
*/
fn main() {
let movable = Box::new(3);
let consume = || {
println!("`movable`: {:?}", movable);
take(movable);
};
consume();
consume();
}
fn take<T>(_v: T) {}
```
For comparison, the following code has no error:
```rust
fn main() {
let movable = Box::new(3);
let consume = move || {
println!("`movable`: {:?}", movable);
};
consume();
consume();
}
```
## Type inferred
The following four closures has no difference in input and return types.
```rust
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v2 = |x: u32| -> u32 { x + 1 };
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
```
4、🌟
```rust,editable
fn main() {
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
/* Make it work, only changeg the following line */
let n = example_closure(5);
}
```
## Fn, FnMut, FnOnce
When taking a closure as an input parameter, the closure's complete type must be annotated using one of the following traits:
- Fn: the closure uses the captured value by reference (&T)
- FnMut: the closure uses the captured value by mutable reference (&mut T)
- FnOnce: the closure uses the captured value by value (T)
5、🌟🌟
```rust,editable
/* Make it work by change the trait bound, in two ways*/
fn fn_once<F>(func: F)
where
F: FnOnce(usize) -> bool,
{
println!("{}", func(3));
println!("{}", func(4));
}
fn main() {
let x = vec![1, 2, 3];
fn_once(|z|{z == x.len()})
}
```
6、 🌟🌟
```rust,editable
fn main() {
let mut s = String::new();
let update_string = |str| s.push_str(str);
exec(update_string);
println!("{:?}",s);
}
/* Fill in the blank */
fn exec<'a, F: __>(mut f: F) {
f("hello")
}
```
#### How does the compiler determine the trait
- Fn: the closure uses the captured value by reference (&T)
- FnMut: the closure uses the captured value by mutable reference (&mut T)
- FnOnce: the closure uses the captured value by value (T)
On a variable-by-variable basis, the compiler will capture variables in the least restrictive manner possible.
For instance, consider a parameter annotated as FnOnce. This specifies that the closure may capture by `&T`, `&mut T`, or `T`, but the compiler will ultimately choose based on how the captured variables are used in the closure.
Which trait to use is determined by what the closure does with captured value.
This is because if a move is possible, then any type of borrow should also be possible. Note that the reverse is not true. If the parameter is annotated as `Fn`, then capturing variables by `&mut T` or `T` are not allowed.
7、🌟🌟
```rust,editable
/* Fill in the blank */
// A function which takes a closure as an argument and calls it.
// <F> denotes that F is a "Generic type parameter"
fn apply<F>(f: F) where
// The closure takes no input and returns nothing.
F: __ {
f();
}
// A function which takes a closure and returns an `i32`.
fn apply_to_3<F>(f: F) -> i32 where
// The closure takes an `i32` and returns an `i32`.
F: Fn(i32) -> i32 {
f(3)
}
fn main() {
use std::mem;
let greeting = "hello";
// A non-copy type.
// `to_owned` creates owned data from borrowed one
let mut farewell = "goodbye".to_owned();
// Capture 2 variables: `greeting` by reference and
// `farewell` by value.
let diary = || {
// `greeting` is by reference: requires `Fn`.
println!("I said {}.", greeting);
// Mutation forces `farewell` to be captured by
// mutable reference. Now requires `FnMut`.
farewell.push_str("!!!");
println!("Then I screamed {}.", farewell);
println!("Now I can sleep. zzzzz");
// Manually calling drop forces `farewell` to
// be captured by value. Now requires `FnOnce`.
mem::drop(farewell);
};
// Call the function which applies the closure.
apply(diary);
// `double` satisfies `apply_to_3`'s trait bound
let double = |x| 2 * x;
println!("3 doubled: {}", apply_to_3(double));
}
```
move closures may still implement `Fn` or `FnMut`, even though they capture variables by move. This is because the traits implemented by a closure type are determined by what the closure does with captured values, not how it captures them. The `move` keyword only specifies the latter.
```rust
fn main() {
let s = String::new();
let update_string = move || println!("{}",s);
exec(update_string);
}
fn exec<F: FnOnce()>(f: F) {
f()
}
```
The following code also has no error:
```rust
fn main() {
let s = String::new();
let update_string = move || println!("{}",s);
exec(update_string);
}
fn exec<F: Fn()>(f: F) {
f()
}
```
8、🌟🌟
```rust,editable
/* Fill in the blank */
fn main() {
let mut s = String::new();
let update_string = |str| -> String {s.push_str(str); s };
exec(update_string);
}
fn exec<'a, F: __>(mut f: F) {
f("hello");
}
```
## Input functions
Since closure maybe used as arguments, you might wonder can we use functions as arguments too? And indeed they can.
9、🌟🌟
```rust,editable
/* Implement `call_me` to make it work */
fn call_me {
f();
}
fn function() {
println!("I'm a function!");
}
fn main() {
let closure = || println!("I'm a closure!");
call_me(closure);
call_me(function);
}
```
## Closure as return types
Returning a closure is much harder than you may thought of.
<!-- 2、
下面代码是Rust圣经课程中[闭包](http://course.rs/advance/functional-programing/closure.html#结构体中的闭包)章节的课内练习题答案:
```rust
@ -48,4 +378,4 @@ fn call_with_different_values() {
assert_eq!(v2, 1);
}
```
``` -->

View File

@ -1 +1,5 @@
# Functional programing
Learning resources:
- English: [Rust Book 13](https://doc.rust-lang.org/book/ch13-00-functional-features.html)
- 简体中文: [Rust语言圣经 - 函数式编程:闭包和迭代器](https://course.rs/advance/functional-programing/intro.html)

View File

@ -1,51 +0,0 @@
# Closure
下面代码是Rust圣经课程中[闭包](http://course.rs/advance/functional-programing/closure.html#结构体中的闭包)章节的课内练习题答案:
```rust
struct Cacher<T,E>
where
T: Fn(E) -> E,
E: Copy
{
query: T,
value: Option<E>,
}
impl<T,E> Cacher<T,E>
where
T: Fn(E) -> E,
E: Copy
{
fn new(query: T) -> Cacher<T,E> {
Cacher {
query,
value: None,
}
}
fn value(&mut self, arg: E) -> E {
match self.value {
Some(v) => v,
None => {
let v = (self.query)(arg);
self.value = Some(v);
v
}
}
}
}
fn main() {
}
#[test]
fn call_with_different_values() {
let mut c = Cacher::new(|a| a);
let v1 = c.value(1);
let v2 = c.value(2);
assert_eq!(v2, 1);
}
```

View File

@ -1 +0,0 @@
# Functional Programming

View File

@ -0,0 +1,234 @@
1、
```rust
fn main() {
let color = String::from("green");
let print = || println!("`color`: {}", color);
print();
print();
println!("{}",color);
}
```
2、
```rust
fn main() {
let mut count = 0;
let mut inc = move || {
count += 1;
println!("`count`: {}", count);
};
inc();
let _reborrow = &count;
inc();
// The closure no longer needs to borrow `&mut count`. Therefore, it is
// possible to reborrow without an error
let _count_reborrowed = &mut count;
assert_eq!(count, 0);
}
```
3、
```rust
fn main() {
// A non-copy type.
let movable = Box::new(3);
// A copy type would copy into the closure leaving the original untouched.
// A non-copy must move and so `movable` immediately moves into
// the closure.
let consume = || {
println!("`movable`: {:?}", movable);
take(movable);
};
consume();
// consume();
}
fn take<T>(_v: T) {
}
```
```rust
fn main() {
// A non-copy type.
let movable = Box::new(3);
// A copy type would copy into the closure leaving the original untouched.
// A non-copy must move and so `movable` immediately moves into
// the closure.
let consume = || {
println!("`movable`: {:?}", movable);
take(&movable);
};
consume();
consume();
}
fn take<T>(_v: &T) {
}
```
4、
```rust
fn main() {
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
/* Make it work, only changeg the following line */
let n = example_closure(String::from("5"));
}
```
5、
```rust
fn fn_once<F>(func: F)
where
F: Fn(usize) -> bool,
{
println!("{}", func(3));
println!("{}", func(4));
}
fn main() {
let x = vec![1, 2, 3];
fn_once(|z|{z == x.len()})
}
```
```rust
fn fn_once<F>(func: F)
where
F: FnOnce(usize) -> bool + Copy,// 改动在这里
{
println!("{}", func(3));
println!("{}", func(4));
}
fn main() {
let x = vec![1, 2, 3];
fn_once(|z|{z == x.len()})
}
```
6、
```rust
fn main() {
let mut s = String::new();
let update_string = |str| s.push_str(str);
exec(update_string);
println!("{:?}",s);
}
fn exec<'a, F: FnMut(&'a str)>(mut f: F) {
f("hello")
}
```
7、
```rust
// A function which takes a closure as an argument and calls it.
// <F> denotes that F is a "Generic type parameter"
fn apply<F>(f: F) where
// The closure takes no input and returns nothing.
F: FnOnce() {
f();
}
// A function which takes a closure and returns an `i32`.
fn apply_to_3<F>(f: F) -> i32 where
// The closure takes an `i32` and returns an `i32`.
F: Fn(i32) -> i32 {
f(3)
}
fn main() {
use std::mem;
let greeting = "hello";
// A non-copy type.
// `to_owned` creates owned data from borrowed one
let mut farewell = "goodbye".to_owned();
// Capture 2 variables: `greeting` by reference and
// `farewell` by value.
let diary = || {
// `greeting` is by reference: requires `Fn`.
println!("I said {}.", greeting);
// Mutation forces `farewell` to be captured by
// mutable reference. Now requires `FnMut`.
farewell.push_str("!!!");
println!("Then I screamed {}.", farewell);
println!("Now I can sleep. zzzzz");
// Manually calling drop forces `farewell` to
// be captured by value. Now requires `FnOnce`.
mem::drop(farewell);
};
// Call the function which applies the closure.
apply(diary);
// `double` satisfies `apply_to_3`'s trait bound
let double = |x| 2 * x;
println!("3 doubled: {}", apply_to_3(double));
}
```
8、
```rust
fn main() {
let mut s = String::new();
let update_string = |str| -> String {s.push_str(str); s };
exec(update_string);
}
fn exec<'a, F: FnOnce(&'a str) -> String>(mut f: F) {
f("hello");
}
```
9、
```rust
// Define a function which takes a generic `F` argument
// bounded by `Fn`, and calls it
fn call_me<F: Fn()>(f: F) {
f();
}
// Define a wrapper function satisfying the `Fn` bound
fn function() {
println!("I'm a function!");
}
fn main() {
// Define a closure satisfying the `Fn` bound
let closure = || println!("I'm a closure!");
call_me(closure);
call_me(function);
}
```