第28回です。前回はこちら。
[第28回の様子]
2022/02/09に第28回を開催した。
内容としてはRust By Example 日本語版の「9.2.4. 関数を受け取る関数」、「9.2.5. クロージャを返す関数」、「9.2.6. stdにおける使用例」に取り組んだ。
しばらく続いていたクロージャのシリーズがこれでひと段落。
参加者は7人。安定して5人以上参加者がいて嬉しい😄
ちなみに、今回の範囲(と前々回あたりの範囲)がまだ翻訳されていなかったので、Rust dojoのメンバーがプルリクを出してくれていた。
今回の未翻訳の箇所はこちらを見ながら進められたので、とても助かった。
[学んだこと]
- 9.2.4. 関数を受け取る関数
- 前回が、クロージャを受け取る関数だったので、関数を受け取るもいけるよね、って話
- こんなかんじでいける
fn call_me<F: Fn()>(f: F) { f(); } fn function() { println!("I'm a function!"); } fn main() { let closure = || println!("I'm a closure!"); call_me(closure); // I'm a closure! call_me(function); // I'm a function! }
- ちなみに、関数の引数や戻り値の型指定は、こんなかんじでいける
fn call_me_with_args<F: Fn(i32) -> i32>(f: F) -> i32 { f(2) } fn function_with_args(n: i32) -> i32 { println!("I'm a function! {}", n); n + 1 } fn main() { let closure_with_args = |n: i32| { println!("I'm a closure! {}", n); n + 1 }; let c_res = call_me_with_args(closure_with_args); // I'm a closure! 2 println!("c_res {}", c_res); // c_res 3 let f_res = call_me_with_args(function_with_args); // I'm a function! 2 println!("f_res {}", f_res); // f_res 3 }
- 9.2.5. クロージャを返す関数
- クロージャを関数の戻り値にすることもできる
- その場合の型指定は
impl Trait
(TraitはFnとかFnMut)
fn create_fn() -> impl Fn() { let text = "Fn".to_owned(); move || println!("This is a: {}", text) } fn main() { let fn_plain = create_fn(); fn_plain(); // This is a: Fn }
fn create_fn2(n: i32) -> impl Fn() { let text = "Fn".to_owned(); move || println!("This is a: {} {}", text, n) } fn main() { let fn_plain2= create_fn2(2); fn_plain2(); // This is a: Fn 2 }
- 9.2.6. stdにおける使用例
- クロージャの使い方として、stdライブラリを見ていく
- まずは
Iterator::any
:シグネチャはこうなっている
pub trait Iterator { // イテレートされる値の型 type Item; fn any<F>(&mut self, f: F) -> bool where // FnMutなので、クロージャによって補足される変数が変更されるが、 // FnOnceではないので、メモリが解放されるわけではない F: FnMut(Self::Item) -> bool {} // FnMutの引数がSelf::Itemなので、参照ではなく値として取る }
- 使い方はこのようになる
fn main() { let vec1 = vec![1, 2, 3]; let vec2 = vec![4, 5, 6]; // ベクトル型に対する`iter`は`&i32`を返す println!("2 in vec1: {}", vec1.iter() .any(|&x| x == 2)); // 2 in vec1: true // `into_iter()`の場合は`i32`を返す println!("2 in vec2: {}", vec2.into_iter().any(| x| x == 2)); // 2 in vec2: false }
Iterator::find
の例も載っていた- こちらはクロージャの引数が値ではなく参照となる点が
Iterator::any
と異なる
fn main() { let vec1 = vec![1, 2, 3]; let vec2 = vec![4, 5, 6]; // `iter()` for vecs yields `&i32`を返す let mut iter = vec1.iter(); // `inter_iter()`の場合は`i32`を返す let mut into_iter = vec2.into_iter(); // &i32のリファレンスは`&&i32`となる println!("Find 2 in vec1: {:?}", iter .find(|&&x| x == 2)); // Find 2 in vec1: Some(2) // `into_iter`の場合は`&i32`が要素の参照 println!("Find 2 in vec2: {:?}", into_iter.find(| &x| x == 2)); // Find 2 in vec2: None
Iterator::find
は要素そのものを返すので、インデックスが必要ならIterator::position
を使う
fn main() { let vec = vec![1, 9, 3, 3, 13, 2]; let index_of_first_even_number = vec.iter().position(|x| x % 2 == 0); pritnln!("{:?}", index_of_first_even_number); // Some(5) }
[まとめ]
モブプログラミングスタイルでRust dojoを開催した。
先週までの蓄積があったので、今週の内容は比較的簡単に理解できた気がする!
今週のプルリクエストはこちら。