0Pricing
Learn Rust Coding · レッスン

Fn、FnMut、FnOnce

クロージャトレイト

「Fn、FnMut、FnOnce」はCoddyKit上の無料Learn Rust Codingレッスンです。 これはレッスン4/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLearn Rust Coding学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Learn Rust Codingコースには全4レッスンが含まれています。

クロージャのトレイト

すべてのクロージャは、キャプチャした値の使い方に応じて、次の3つのトレイトのうち1つ以上を自動的に実装します:

  • Fn — 不変で借用します。
  • FnMut — 可変で借用します。
  • FnOnce — 所有権を取得し、1回だけ呼び出せます。
fn main() {
    let x = 5;
    let show = || println!("x is {}", x); // implements Fn
    show();
    show();
}

Fn:不変借用

キャプチャした値を読み取るだけのクロージャはFnを実装します。何も変更せずに何度でも呼び出せます。

fn main() {
    let greeting = String::from("Hi");
    let say = || println!("{}", greeting);
    say();
    say();
}

FnMut:可変借用

キャプチャした値を変更するクロージャはFnMutを実装します。呼び出すには、クロージャの変数自体をmutにする必要があります。

fn main() {
    let mut count = 0;
    let mut increment = || {
        count += 1;
        println!("count {}", count);
    };
    increment();
    increment();
}

FnOnce:所有権の取得

キャプチャした値を環境の外へムーブするクロージャはFnOnceを実装します。呼び出せるのは1回だけです。

fn main() {
    let name = String::from("Ada");
    let consume = move || {
        let owned = name;
        println!("consumed {}", owned);
    };
    consume();
}

トレイトの階層

トレイトには階層関係があります:

  • すべてのFnはFnMutとFnOnceでもあります。
  • すべてのFnMutはFnOnceでもあります。

つまり、Fnが最も柔軟で、FnOnceが最も制限されています。

fn main() {
    let value = 42;
    let read = || value; // Fn, so also FnMut and FnOnce
    println!("{}", read());
}

関数にFnを渡す

関数は、必要なトレイトを指定することでクロージャを受け取れます。トレイト境界付きのジェネリクスを使用します。

fn apply<F: Fn(i32) -> i32>(f: F, x: i32) -> i32 {
    f(x)
}

fn main() {
    let double = |n| n * 2;
    println!("{}", apply(double, 9));
}

FnMutを受け取る

関数でキャプチャした状態を変更する必要がある場合は、FnMutを要求し、パラメーターにmutを付けます。

fn call_twice<F: FnMut()>(mut f: F) {
    f();
    f();
}

fn main() {
    let mut total = 0;
    call_twice(|| total += 1);
    // note: total is borrowed during the call
}

FnOnceを受け取る

関数がクロージャを1回しか呼び出さず、値を消費する可能性がある場合は、FnOnceを要求します。

fn run_once<F: FnOnce() -> String>(f: F) {
    let result = f();
    println!("{}", result);
}

fn main() {
    let s = String::from("done");
    run_once(move || s);
}

moveキーワード

moveキーワードを使うと、クロージャはキャプチャした変数を借用せず、所有権を取得します。

これは、スレッド内など、クロージャが作成されたスコープより長く存続する必要がある場合に不可欠です。

fn main() {
    let data = vec![1, 2, 3];
    let printer = move || println!("{:?}", data);
    printer();
}

Rustがトレイトを選ぶ仕組み

トレイトを手動で選ぶ必要はありません。コンパイラーがクロージャの本体に基づいて決定します:

  • 読み取るだけ → Fn
  • 変更する → FnMut
  • キャプチャした値を外へムーブする → FnOnce
fn main() {
    let mut log = Vec::new();
    let mut record = |x: i32| log.push(x); // FnMut
    record(1);
    record(2);
    println!("{:?}", log);
}

適切な境界の選択

原則として:

  • 呼び出して読み取るだけならFnを受け取ります。
  • クロージャが状態を変更するならFnMutを受け取ります。
  • 1回呼び出せば十分ならFnOnceを受け取ります。

条件に合う中で、最も制限の少ない境界を選びます。

fn each<F: Fn(i32)>(items: &[i32], f: F) {
    for &i in items {
        f(i);
    }
}

fn main() {
    each(&[1, 2, 3], |n| println!("got {}", n));
}

クイックチェック

クロージャの動作とトレイトを対応させましょう。

まとめ

クロージャの3つのトレイト:

  • Fn — 不変で借用し、繰り返し呼び出せます。
  • FnMut — 可変で借用し、状態を変更できます。
  • FnOnce — 所有権を取得し、1回だけ呼び出せます。

トレイトはコンパイラーが推論し、moveは所有権のキャプチャを強制します。

fn main() {
    let base = 5;
    let adder = move |x: i32| x + base; // Fn, captures base by move
    println!("{}", adder(10));
}

よくある質問

「Fn、FnMut、FnOnce」レッスンは無料ですか?

はい。「Fn、FnMut、FnOnce」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Learn Rust Codingコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Learn Rust Codingコースには全4レッスンが含まれています。

「Fn、FnMut、FnOnce」で何を学びますか?

クロージャトレイト ブラウザで直接実行するハンズオンコードでLearn Rust Codingを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Learn Rust Codingを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLearn Rust Codingは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン4/4です。

「Fn、FnMut、FnOnce」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLearn Rust Codingレッスンでコードを書いて実行できますか?

はい。すべてのLearn Rust Codingレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. 関数の定義
  2. 式と文
  3. クロージャ
  4. Fn、FnMut、FnOnce
← Learn Rust Codingに戻る