クロージャを受け取る関数
クロージャが周辺の環境から変数を取得するやり方は非常に明瞭です。何か注意すべき点はあるのでしょうか? もちろんです。関数内でクロージャを使う場合、[ジェネリック]型を使用する必要があります。詳しく見ていきましょう。
#![allow(unused)] fn main() { // `F` must be generic. // `F` はジェネリック型でなくてはならない fn apply<F>(f: F) where F: FnOnce() { f(); } }
クロージャが定義されると、コンパイラは裏側で、無名の構造体を作り、そこにクロージャによって使用される外側の変数を入れます。同時にFn
、FnMut
、FnOnce
という名のトレイトのいずれか一つを介してこの構造体に関数としての機能を実装し、実際に呼び出されるまで待ちます。
この無名構造体は型が未指定(unknown
)なため、関数を実行する際にはジェネリクスが必要とされます。とはいえ、<T>
で指定するだけでは、まだ曖昧です。(訳注: &self
、&mut self
、self
のいずれをとるのかがわからないため)そのため、Fn
、FnMut
、FnOnce
のいずれか一つを実装することで対応しています。
// `F` must implement `Fn` for a closure which takes no // inputs and returns nothing - exactly what is required // for `print`. // `F`は`Fn`を実装していなくてはならず、`Fn`は引数と返り値を持たない。 // `print`は文字をプリントするだけのクロージャなので、これが正しい。 fn apply<F>(f: F) where F: Fn() { f(); } fn main() { let x = 7; // Capture `x` into an anonymous type and implement // `Fn` for it. Store it in `print`. // `x`を無名の構造体に入れ、それに対し`Fn`を実装する。 // (訳注: ここでは`Fn`は`fn Fn(&self) -> {println!("{}", &self)}`) // その構造体を`print`にアサインする。 let print = || println!("{}", x); apply(print); }
参照
A thorough analysis, Fn
, FnMut
,
and FnOnce