球魚
Closures 閉包
Anonymous Functions
Function 的寫法
let add_one_v2 = |x: u32| -> u32 { x + 1 };
Closures 的寫法
fn add_one_v1 (x: u32) -> u32 { x + 1 }
let add_one_v3 = |x| { x + 1 };
let add_one_v4 = |x| x + 1 ;
fn main () {
let example_closure = |x| x;
let s = example_closure(String::from("hello"));
let n = example_closure(5);
}
如果不寫型態的話
會依第一次呼叫時,傳進來的型態為依據
之後的呼叫要和第一次傳進來的型態一樣
error[E0308]: mismatched types
--> src/main.rs:5:29
|
5 | let n = example_closure(5);
| ^
| |
| expected struct `std::string::String`, found integral variable
| help: try using a conversion method: `5.to_string()`
|
= note: expected type `std::string::String`
found type `{integer}`
Capturing the Environment with Closures
fn main() {
let x = 4;
let equal_to_x = |z| z == x;
let y = 4;
assert!(equal_to_x(y));
}
We can’t do the same with functions
fn main() {
let x = 4;
fn equal_to_x(z: i32) -> bool { z == x }
let y = 4;
assert!(equal_to_x(y));
}
error[E0434]: can't capture dynamic environment in a fn item
--> src/main.rs:4:42
|
4 | fn equal_to_x(z: i32) -> bool { z == x }
| ^
|
= help: use the `|| { ... }` closure form instead
If you want to force the closure to take ownership of the values it uses in the environment, you can use the "move" keyword
fn main() {
let x = vec![1, 2, 3];
let equal_to_x = move |z| z == x;
println!("can't use x here: {:?}", x);
let y = vec![1, 2, 3];
assert!(equal_to_x(y));
}
error[E0382]: use of moved value: `x`
--> src/main.rs:6:40
|
4 | let equal_to_x = move |z| z == x;
| -------- value moved (into closure) here
5 |
6 | println!("can't use x here: {:?}", x);
| ^ value used here after move
|
= note: move occurs because `x` has type `std::vec::Vec<i32>`, which does not implement
the `Copy` trait
既然可以 capture environment
就想實驗看看,在 closure 裡改值會發生什麼事
fn main() {
let mut x = 1;
let mut add_to_x = move || {
x = x + 1;
println!("Fn x {:?}", x);
};
println!("x {:?}", x);
add_to_x();
println!("x {:?}", x);
add_to_x();
println!("x {:?}", x);
}
Compiling closure v0.1.0 (file:///home/ballfish/rusts/closure)
Finished dev [unoptimized + debuginfo] target(s) in 1.50 secs
x 1
Fn x 2
x 1
Fn x 3
x 1
Fn traits
Storing Closures Using Generic Parameters and the Fn Traits
struct Cacher<T>
where T: Fn(u32) -> u32
{
calculation: T,
value: Option<u32>,
}
impl<T> Cacher<T>
where T: Fn(u32) -> u32
{
fn new(calculation: T) -> Cacher<T> {
Cacher {
calculation,
value: None,
}
}
fn value(&mut self, arg: u32) -> u32 {
match self.value {
Some(v) => v,
None => {
let v = (self.calculation)(arg);
self.value = Some(v);
v
},
}
}
}
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, 2);
}
其他的 traits
- FnOnce consumes the variables it captures from its enclosing scope, known as the closure’s environment. To consume the captured variables, the closure must take ownership of these variables and move them into the closure when it is defined. The Once part of the name represents the fact that the closure can’t take ownership of the same variables more than once, so it can be called only once.
- FnMut can change the environment because it mutably borrows values.
- Fn borrows values from the environment immutably.
Iterators 迭代
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
for val in v1_iter {
println!("Got: {}", val);
}
for 的方法
The Iterator Trait and the next Method
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
fn iterator_demonstration() {
let v1 = vec![1, 2, 3];
let mut v1_iter = v1.iter();
assert_eq!(v1_iter.next(), Some(&1));
assert_eq!(v1_iter.next(), Some(&2));
assert_eq!(v1_iter.next(), Some(&3));
assert_eq!(v1_iter.next(), None);
}
fn iterator_sum() {
let v1 = vec![1, 2, 3];
let v1_iter = v1.iter();
let total: i32 = v1_iter.sum();
assert_eq!(total, 6);
}
let v1: Vec<i32> = vec![1, 2, 3];
let v2: Vec<_> = v1.iter().map(|x| x + 1).collect();
assert_eq!(v2, vec![2, 3, 4]);
#[derive(PartialEq, Debug)]
struct Shoe {
size: u32,
style: String,
}
fn shoes_in_my_size(shoes: Vec<Shoe>, shoe_size: u32) -> Vec<Shoe> {
shoes.into_iter()
.filter(|s| s.size == shoe_size)
.collect()
}
#[test]
fn filters_by_size() {
let shoes = vec![
Shoe { size: 10, style: String::from("sneaker") },
Shoe { size: 13, style: String::from("sandal") },
Shoe { size: 10, style: String::from("boot") },
];
let in_my_size = shoes_in_my_size(shoes, 10);
assert_eq!(
in_my_size,
vec![
Shoe { size: 10, style: String::from("sneaker") },
Shoe { size: 10, style: String::from("boot") },
]
);
}
實作 Iterator trait
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
self.count += 1;
if self.count < 6 {
Some(self.count)
} else {
None
}
}
}
let sum: u32 = Counter::new().zip(Counter::new().skip(1))
.map(|(a, b)| a * b)
.filter(|x| x % 3 == 0)
.sum();
assert_eq!(18, sum);
應用 Counter
pair (5, None) 因為 zip 會把 pair 成員中有 None 的 pair 視為 None 回傳,所以 (5, None) 不會被計算
Comparing Performance: Loops vs. Iterators
- test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
- test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
Rust
By 球魚
Rust
Functional Language Features: Iterators and Closures
- 950