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フィードバックを取得できます。ローカル設定は不要です。