入力パラメータとして

RustRustBeginner
今すぐ練習

This tutorial is from open-source community. Access the source code

💡 このチュートリアルは英語版からAIによって翻訳されています。原文を確認するには、 ここをクリックしてください

はじめに

この実験では、クロージャを入力パラメータとして受け取る Rust の関数を書く際、クロージャの完全な型を、クロージャがキャプチャされた値を参照、可変参照、または値でどのように使用するかを決定する FnFnMut、または FnOnce のいずれかの トレイト を使用して注釈付けする必要があることを学びます。コンパイラは、クロージャに選択されたトレイトに基づいて、可能な限り制限の少ない方法で変数をキャプチャします。

注: 実験でファイル名が指定されていない場合、好きなファイル名を使用できます。たとえば、main.rs を使用して、rustc main.rs &&./main でコンパイルして実行できます。

入力パラメータとして

Rust は、型注釈なしでほとんど即座に変数をキャプチャする方法を選択しますが、関数を書く際にはこの曖昧さは許されません。クロージャを入力パラメータとして受け取る場合、クロージャの完全な型をいくつかの トレイト のいずれかを使用して注釈付けする必要があり、それらはクロージャがキャプチャされた値をどのように使用するかによって決まります。制限の減少する順に並べると、次のとおりです。

  • Fn:クロージャは参照 (&T) でキャプチャされた値を使用します
  • FnMut:クロージャは可変参照 (&mut T) でキャプチャされた値を使用します
  • FnOnce:クロージャは値 (T) でキャプチャされた値を使用します

変数ごとに、コンパイラは可能な限り制限の少ない方法で変数をキャプチャします。

たとえば、FnOnce として注釈付けされたパラメータを考えてみましょう。これは、クロージャが &T&mut T、または T でキャプチャする 可能性がある ことを指定しますが、コンパイラは最終的にクロージャ内でキャプチャされた変数がどのように使用されるかに基づいて選択します。

これは、ムーブが可能な場合、どんな種類の借用も可能であるはずだからです。逆は正しくありません。パラメータが Fn として注釈付けされている場合、&mut T または T で変数をキャプチャすることは許されません。ただし、&T は許されます。

次の例では、FnFnMut、および FnOnce の使用を入れ替えて、何が起こるか見てみましょう。

// クロージャを引数として受け取り、それを呼び出す関数。
// <F> は、F が「ジェネリック型パラメータ」であることを示します
fn apply<F>(f: F) where
    // クロージャは入力を取らず、何も返さない。
    F: FnOnce() {
    // ^ TODO: これを `Fn` または `FnMut` に変更してみてください。

    f();
}

// クロージャを取り、`i32` を返す関数。
fn apply_to_3<F>(f: F) -> i32 where
    // クロージャは `i32` を取り、`i32` を返す。
    F: Fn(i32) -> i32 {

    f(3)
}

fn main() {
    use std::mem;

    let greeting = "hello";
    // コピーできない型。
    // `to_owned` は借用されたデータから所有されたデータを作成します
    let mut farewell = "goodbye".to_owned();

    // 2 つの変数をキャプチャする:参照で `greeting` と
    // 値で `farewell`。
    let diary = || {
        // `greeting` は参照で:`Fn` が必要。
        println!("I said {}.", greeting);

        // ミューテーションにより、`farewell` が
        // 可変参照でキャプチャされるようになります。これで `FnMut` が必要になります。
        farewell.push_str("!!!");
        println!("Then I screamed {}.", farewell);
        println!("Now I can sleep. zzzzz");

        // 手動で drop を呼び出すことで、`farewell` が
        // 値でキャプチャされるようになります。これで `FnOnce` が必要になります。
        mem::drop(farewell);
    };

    // クロージャを適用する関数を呼び出す。
    apply(diary);

    // `double` は `apply_to_3` のトレイト境界を満たします
    let double = |x| 2 * x;

    println!("3 doubled: {}", apply_to_3(double));
}

まとめ

おめでとうございます!あなたは「入力パラメータとして」の実験を完了しました。あなたの技術を向上させるために、LabEx でさらに実験を行って練習してください。