やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Rustのクロージャ

 無名関数。

成果物

コード

クロージャの定義

let add_one = |num| {
    num + 1
};

 変数add_oneクロージャが保存された。|num|は関数の仮引数。複数のときは|param1, param2|のように書く。

 呼出は関数と同様に。

add_one(100);

クロージャ型推論と注釈

  • 引数と戻り値の型は不要

 だが注釈することもできる。

let add_one = |num: u32| -> u32 {
    num + 1
};

 以下、fnと似たように書ける。また、省略もできる。

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  ;

 推論する型は最初の呼出で決まる。

let example_closure = |x| x;

let s = example_closure(String::from("hello"));
let n = example_closure(5);

 2回目の呼出でエラー。1回目で引数の型がStringと決まった。なのに2回目の呼出で引数がi32型だったため。

error[E0308]: mismatched types
 --> src/main.rs:7:29
  |
7 |     let n = example_closure(5);
  |                             ^
  |                             |
  |                             expected struct `std::string::String`, found integer
  |                             help: try using a conversion method: `5.to_string()`
  |
  = note: expected type `std::string::String`
             found type `{integer}`

クロージャのキャッシュ

Fnトレイト

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 main() {
    let x = 4;
    let equal_to_x = |z| z == x;
    let y = 4;
    assert!(equal_to_x(y));
}

 クロージャは宣言された親関数のスコープにある変数を参照できる。

 関数でこれをやるとエラーになる。

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:3:42
  |
3 |     fn equal_to_x(z: i32) -> bool { z == x }
  |                                          ^
  |
  = help: use the `|| { ... }` closure form instead

Fnトレイト3種類

 Fnトレイトには以下3種類ある。

トレイト 環境
Fn 不変借用
FnMut 可変借用
FnOnce 所有権を奪う

move

fn main() {
    let x = vec![1, 2, 3];
    let equal_to_x = move |z| z == x;
    // ここでは、xを使用できません: {:?}
    println!("can't use x here: {:?}", x);
    let y = vec![1, 2, 3];
    assert!(equal_to_x(y));
}

対象環境

$ uname -a
Linux raspberrypi 4.19.42-v7+ #1219 SMP Tue May 14 21:20:58 BST 2019 armv7l GNU/Linux

前回まで