Rust by Example

8.2.3 クロージャを受け取る関数

クロージャが周辺の環境から変数を取得するやり方は非常に明瞭です。何か注意すべき点はあるのでしょうか?

もちろんです。関数内でクロージャを使う場合、ジェネリック型を使用する必要があります。詳しく見ていきましょう。

// `F` はジェネリック型でなくてはならない
fn apply<F>(f: F) where
    F: FnOnce() {
    f()
}

クロージャが定義されると、コンパイラは裏側で、無名の構造体を作り、そこにクロージャによって使用される外側の変数を入れます。同時にFnFnMutFnOnceという名のトレイトのいずれか一つを介してこの構造体に関数としての機能を実装し、実際に呼び出されるまで待ちます。

この無名構造体は型が未指定(unknown)なため、関数を実行する際にはジェネリクスが必要とされます。とはいえ、<T>で指定するだけでは、まだ曖昧です。(訳注: &self&mut selfselfのいずれをとるのかがわからないため)そのため、FnFnMutFnOnceのいずれか一つを実装することで対応しています。

// `F`は`Fn`を実装していなくてはならず、`Fn`は引数と返り値を持たない。 // `print`は文字をプリントするだけのクロージャなので、これが正しい。 fn apply<F>(f: F) where F: Fn() { f() } fn main() { let x = 7; // `x`を無名の構造体に入れ、それに対し`Fn`を実装する。 // (訳注: ここでは`Fn`は`fn Fn(&self) -> {println!("{}", &self)}`) // その構造体を`print`にアサインする。 let print = || println!("{}", x); apply(print); }

See also:

A thorough analysis, Fn, FnMut, and FnOnce