球魚

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

  • 970