匿名型
クロージャが周辺の環境から変数を取得するやり方は非常に明瞭です。何か注意すべき点はあるのでしょうか?もちろんです。関数内でクロージャを使う場合、ジェネリクスを使用する必要があります。詳しく見ていきましょう。
#![allow(unused)] fn main() { // `F` はジェネリック型でなくてはなりません。 fn apply<F>(f: F) where F: FnOnce() { f(); } }
クロージャが定義されると、コンパイラは裏側で、無名の構造体を作り、そこにクロージャによって使用される外側の変数を入れます。同時にFn
、FnMut
、FnOnce
という名のトレイトのいずれか一つを介してこの構造体に関数としての機能を実装し、実際に呼び出されるまで待ちます。
この無名構造体は型が未指定なため、関数を実行する際にはジェネリクスが必要とされます。とはいえ、<T>
で指定するだけでは、まだ曖昧です。(訳注:&self
、&mut self
、self
のいずれをとるのかがわからないため)そのため、Fn
、FnMut
、FnOnce
のいずれか一つを実装することで対応しています。
// `F`は引数と戻り値を持たないクロージャ`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); }