web-dev-qa-db-ja.com

Rustの構造体にクロージャーを格納するにはどうすればよいですか?

Rust 1.0の前に、この廃止されたクロージャ構文を使用して構造を書くことができました:

struct Foo {
    pub foo: |usize| -> usize,
}

今私は次のようなことができます:

struct Foo<F: FnMut(usize) -> usize> {
    pub foo: F,
}

しかし、私が作成するFooオブジェクトのタイプは何ですか?

let foo: Foo<???> = Foo { foo: |x| x + 1 };

参照を使用することもできます:

struct Foo<'a> {
    pub foo: &'a mut FnMut(usize) -> usize,
}

これは遅いと思います

  1. ポインター逆参照
  2. 実際に使用されるFnMutのタイプに特化はありません
31
bfops

_||_構文を使用した古いスタイルのクロージャーは、スタックに格納されたクロージャーへの参照であり、&'a mut FnMut(usize) -> usizeと同等になりました。古いスタイルのprocsはヒープに割り当てられ、Box<dyn FnOnce(usize) -> usize>と同等でした(procを呼び出すことができるのは1回だけです)。

3番目のコードスニペットで使用するタイプについては、is n't one;があります。クロージャタイプは匿名であり、直接名前を付けることはできません。代わりに、次のように記述します。

_let foo = Foo { foo: |x| x + 1 };
_

needを使用してFooが必要であることを指定するコンテキストでコードを記述している場合は、次のように記述します。

_let foo: Foo<_> = Foo { foo: |x| x + 1 };
_

___は、実際のジェネリック型を推測するよう型システムに指示します。

降順で使用するwhichに関する一般的な経験則:

  • ジェネリックパラメーター:struct Foo<F: FnMut(usize) -> usize>。これは最も効率的ですが、特定のFooインスタンスが格納できるのはoneクロージャのみであることを意味します。すべてのクロージャが異なる具象型を持っているためです。
  • 特性参照:&'a mut dyn FnMut(usize) -> usize。ポインターの間接指定がありますが、互換性のある呼び出しシグニチャーを持つ任意のクロージャーへの参照を保管できるようになりました。
  • ボックス化クロージャー:Box<dyn FnMut(usize) -> usize>。これには、ヒープにクロージャを割り当てることが含まれますが、ライフタイムについて心配する必要はありません。参照と同様に、互換性のあるシグネチャを持つ任意のクロージャを保存できます。
30
DK.

デモンストレーションのために、既存の回答をさらにいくつかのコードで補完します。

箱なしの閉鎖

ジェネリック型を使用します。

struct Foo<F>
where
    F: Fn(usize) -> usize,
{
    pub foo: F,
}

fn main() {
    let foo = Foo { foo: |a| a + 1 };
    (foo.foo)(42);
}

ボックス化された特性オブジェクト

struct Foo {
    pub foo: Box<dyn Fn(usize) -> usize>,
}

fn main() {
    let foo = Foo {
        foo: Box::new(|a| a + 1),
    };
    (foo.foo)(42);
}

特性オブジェクト参照

struct Foo<'a> {
    pub foo: &'a dyn Fn(usize) -> usize,
}

fn main() {
    let foo = Foo { foo: &|a| a + 1 };
    (foo.foo)(42);
}

関数ポインタ

struct Foo {
    pub foo: fn(usize) -> usize,
}

fn main() {
    let foo = Foo { foo: |a| a + 1 };
    (foo.foo)(42);
}

作成したFooオブジェクトのタイプは何ですか?

これは、名前を付けることができない、自動的に生成されるタイプです。

参照を低速で使用することもできます[...]ポインタの逆参照[...]特殊化がないため

おそらく、しかしそれは発信者にとってはるかに簡単です。

以下も参照してください。

11
Shepmaster