From 82201436888b35427491bee84607366e3d6240e0 Mon Sep 17 00:00:00 2001 From: sunface Date: Wed, 30 Mar 2022 22:29:07 +0800 Subject: [PATCH] Add [Closure] chapter --- en/src/functional-programing/cloure.md | 332 ++++++++++++++++++++- en/src/functional-programing/intro.md | 4 + en/src/functional-programming/closure.md | 51 ---- en/src/functional-programming/intro.md | 1 - solutions/functional-programing/closure.md | 234 +++++++++++++++ 5 files changed, 569 insertions(+), 53 deletions(-) delete mode 100644 en/src/functional-programming/closure.md delete mode 100644 en/src/functional-programming/intro.md create mode 100644 solutions/functional-programing/closure.md diff --git a/en/src/functional-programing/cloure.md b/en/src/functional-programing/cloure.md index fb73740..7427fcf 100644 --- a/en/src/functional-programing/cloure.md +++ b/en/src/functional-programing/cloure.md @@ -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(_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(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. +// denotes that F is a "Generic type parameter" +fn apply(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) -> 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: 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: 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. + + + \ No newline at end of file diff --git a/en/src/functional-programing/intro.md b/en/src/functional-programing/intro.md index 6f326b8..27cd84b 100644 --- a/en/src/functional-programing/intro.md +++ b/en/src/functional-programing/intro.md @@ -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) + diff --git a/en/src/functional-programming/closure.md b/en/src/functional-programming/closure.md deleted file mode 100644 index fb73740..0000000 --- a/en/src/functional-programming/closure.md +++ /dev/null @@ -1,51 +0,0 @@ -# Closure - -ไธ‹้ขไปฃ็ ๆ˜ฏRustๅœฃ็ป่ฏพ็จ‹ไธญ[้—ญๅŒ…](http://course.rs/advance/functional-programing/closure.html#็ป“ๆž„ไฝ“ไธญ็š„้—ญๅŒ…)็ซ ่Š‚็š„่ฏพๅ†…็ปƒไน ้ข˜็ญ”ๆกˆ๏ผš - -```rust -struct Cacher -where - T: Fn(E) -> E, - E: Copy -{ - query: T, - value: Option, -} - -impl Cacher -where - T: Fn(E) -> E, - E: Copy -{ - fn new(query: T) -> Cacher { - 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); -} -``` \ No newline at end of file diff --git a/en/src/functional-programming/intro.md b/en/src/functional-programming/intro.md deleted file mode 100644 index dca041e..0000000 --- a/en/src/functional-programming/intro.md +++ /dev/null @@ -1 +0,0 @@ -# Functional Programming diff --git a/solutions/functional-programing/closure.md b/solutions/functional-programing/closure.md new file mode 100644 index 0000000..9e56894 --- /dev/null +++ b/solutions/functional-programing/closure.md @@ -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(_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(_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(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(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. +// denotes that F is a "Generic type parameter" +fn apply(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) -> 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: 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); +} +``` \ No newline at end of file