関数ポインタとクロージャー

関数ポインタ

fn do_twice(f: fn(i32) -> i32, arg: i32) -> {
    f(arg) + f(arg)
}

// add_one: fn(i32) -> i32
do_twice(add_one, 1);
  • fn: 関数ポインタを表す
  • 関数の引数にわたすことが出来る
  • 3つのクロージャートレイト(Fn, FnMut, FnOnece)を実装している
  • C関数は関数ポインタのみ引数として渡すことが出来る

mapを使った例

例1: クロージャーを渡す

let list_of_numbers = vec![1, 2, 3];
let list_of_strings = list_of_numbers.iter().map(|i| i.to_string()).collect::<Vec<String>>();

例2: 関数に名前をつけて渡す

let list_of_numbers = vec![1, 2, 3];
let list_of_strings = list_of_numbers.iter().map(ToString::to_string()).collect::<Vec<String>>();
  • ToStringトレイトに実装されたto_stringを使用している
    • Displayトレイトの親トレイト

例3: タプル構造体もしくはenumの生成

enum Status {
    Value(u32),
    Stop,
}

let list_of_statuses: Vec<Status> = (0u32..20).map(Status::Value).collect();
  • Status::Valueの初期化関数を使用することによってRange<u32>からVec<Status>を生成

クロージャーを返す

  • クロージャートレイトはSizedトレイトではない
  • 関数ポインタfnキーワードを返り値の型として使用できない

したがってトレイトオブジェクトとしてクロージャーは返せない

fn return_closure() -> dyn Fn(i32) -> i32 {
    |x| x + 1
}
error[E0746]: return type cannot have an unboxed trait object
  |
1 | fn returns_closure() -> dyn Fn(i32) -> i32 {
  |                         ^^^^^^^^^^^^^^^^^^ doesn't have a size known at compile-time
  |
  = note: for information on `impl Trait`, see <https://doc.rust-lang.org/book/ch10-02-traits.html#returning-types-that-implement-traits>
help: use `impl Fn(i32) -> i32` as the return type, as all return paths are of type `[closure@/Users/yossan/testcodes/rust/garbage/クロージャーは返せない.rs:2:5: 2:14]`, which implements `Fn(i32) -> i32`
  |
1 | fn returns_closure() -> impl Fn(i32) -> i32 {
  |                         ^^^^^^^^^^^^^^^^^^^

クロージャーを返す場合は、implキワードを使うか、Box型に格納するか

fn returns_closure() -> impl Fn(i32) -> i32 {
    |x| x + 1
}

参照

https://doc.rust-lang.org/book/ch19-05-advanced-functions-and-closures.html