Caution

お使いのブラウザはJavaScriptが実行できない状態になっております。
当サイトはWebプログラミングの情報サイトの為、
JavaScriptが実行できない環境では正しいコンテンツが提供出来ません。
JavaScriptが実行可能な状態でご閲覧頂くようお願い申し上げます。

Rust辞典

  1. トップページ
  2. Rust辞典
  3. 関数ポインタ(fn) / 高階関数

関数ポインタ(fn) / 高階関数

Rustでは関数を値として扱う『関数ポインタ(fn型)』が使えます。クロージャと異なり周囲の変数をキャプチャしない点が特徴で、関数を引数として渡す高階関数パターンに活用できます。

構文
// 関数ポインタの型: fn(引数型) -> 戻り値型
fn add(x: i32, y: i32) -> i32 { x + y }
let f: fn(i32, i32) -> i32 = add;

// 関数を引数として受け取る高階関数
fn apply(f: fn(i32) -> i32, x: i32) -> i32 { f(x) }

// 関数ポインタを要素とするベクタ
let funcs: Vec<fn(i32) -> i32> = vec![double, square];

// メソッドを関数ポインタとして渡す(UFCS記法)
let doubled: Vec<i32> = nums.iter().map(|x| x * 2).collect();
let lengths: Vec<usize> = words.iter().map(|s| s.len()).collect();
概念一覧
記法概要
fn(T) -> U関数ポインタ型。キャプチャなし。クロージャより軽量です。
Fn(T) -> Uクロージャトレイト。関数ポインタも実装しています。
map(str::len)既存の関数を直接 map 等に渡せます(UFCS記法)。
Type::method型の関連関数・メソッドを関数ポインタとして使えます。
高階関数関数を引数または戻り値として扱う関数です。
サンプルコード
// 関数ポインタとして扱う関数定義
fn double(x: i32) -> i32 { x * 2 }
fn square(x: i32) -> i32 { x * x }
fn is_even(x: &i32) -> bool { x % 2 == 0 }

// 関数ポインタを引数として受け取る高階関数
fn apply_all(funcs: &[fn(i32) -> i32], x: i32) -> Vec<i32> {
    funcs.iter().map(|f| f(x)).collect()
}

fn main() {
    // 関数ポインタを変数に代入します。
    let f: fn(i32) -> i32 = double;
    println!("double(5) = {}", f(5));  // 10

    // 関数ポインタの配列
    let ops: Vec<fn(i32) -> i32> = vec![double, square];
    let results = apply_all(&ops, 4);
    println!("results: {:?}", results);  // [8, 16]

    // map に既存の関数を直接渡します。
    let words = vec!["hello", "world", "rust"];
    let lengths: Vec<usize> = words.iter().map(str::len).collect();
    println!("lengths: {:?}", lengths);  // [5, 5, 4]

    // filter に関数ポインタを渡します。
    let nums = vec![1, 2, 3, 4, 5, 6];
    let evens: Vec<&i32> = nums.iter().filter(is_even).collect();
    println!("evens: {:?}", evens);  // [2, 4, 6]

    // String::from を map で使います。
    let strs = vec!["a", "b", "c"];
    let owned: Vec<String> = strs.iter().map(|s| s.to_string()).collect();
    println!("owned: {:?}", owned);

    // 関数を返す高階関数(クロージャを返します)
    fn make_adder(n: i32) -> impl Fn(i32) -> i32 {
        move |x| x + n
    }
    let add5 = make_adder(5);
    let add10 = make_adder(10);
    println!("add5(3) = {}", add5(3));   // 8
    println!("add10(3) = {}", add10(3)); // 13

    // コールバックパターン
    fn transform(data: &[i32], callback: fn(i32) -> i32) -> Vec<i32> {
        data.iter().map(|&x| callback(x)).collect()
    }
    let doubled = transform(&[1, 2, 3, 4], double);
    println!("doubled: {:?}", doubled);  // [2, 4, 6, 8]
}
概要

関数ポインタ型『fn(T) -> U』はキャプチャを持たない純粋な関数への参照です。クロージャトレイト『Fn』『FnMut』『FnOnce』とは異なり、スタック上のデータを参照しないため、スレッド間で安全に共有できます。

『map(str::len)』や『filter(is_even)』のように既存の関数を直接イテレータのアダプタに渡せます。これはクロージャ『|s| s.len()』より簡潔に書けることがあります。

関数を返す場合は『impl Fn()』を戻り値型に使うのが便利です(Rustコンパイラが型を推論します)。クロージャでキャプチャが必要な場合は『move』を付けてください。クロージャトレイトの詳細はFn / FnMut / FnOnce トレイトを参照してください。

記事の間違いや著作権の侵害等ございましたらお手数ですがまでご連絡頂ければ幸いです。