S-JIS[2024-09-16/2024-10-13] 変更履歴

Rustクロージャーメモ

Rustのクロージャーのメモ。


概要

クロージャーは、名前の無い関数(無名関数)。
(Javaのラムダ式に相当)

関数やメソッドの引数としてクロージャーを渡したり、クロージャーを返したりすることが出来る。


クロージャーの定義方法

クロージャーは以下のような構文。

|引数名〔: 型〕, …|〔-> 戻り型〕 返り値の式

関数は丸括弧で引数を囲むが、Rustのクロージャーでは縦棒「|」で囲む。

複数の文を書いて最終的な値を返したい場合は、波括弧で囲んだブロックを使う。


クロージャーの型

クロージャーの型はFnトレイトで表す。
(可変な場合はFnMutトレイトを使うなど、クロージャーの型に関するトレイトがいくつかある)

impl | dyn〕Fn(型, …) -> 戻り型

impl・dynを省略した場合はimpl扱いになる。→dynとimplの意味


クロージャーの例

説明 Java相当 Scala相当
引数が無く、何も処理しない
let c = || {};
Runnable c = () -> {};
val c = () => {}
引数が1つで値を返す
let c = |n: i32| n + 1;
IntUnaryOperator c = n -> n + 1;
val c = (n: Int) => n + 1
引数が2つ
let c = |x: i32, y: i32| x + y;
IntBinaryOperator c = (x, y) -> x + y;
val c: = (x: Int, y: Int) => x + y
val c: (Int,Int)=>Int = (x, y) => x + y
関数に渡すときは引数の型を省略できる
fn func<F: Fn(i32) -> i32>(f: F) -> i32 {
    f(123)
}
let r = func(|n| n + 1);
int func(IntUnaryOperator f) {
    return f.applyAsInt(123);
}
int r = func(n -> n + 1);
def func(f: Int=>Int): Int = {
  f(123)
}
val r1 = func(n => n + 1)
val r2 = func(_ + 1)
クロージャーの外の変数を使える
let add = 100
let r = func(|n| n + add);
final int add = 100;
int r = func(n -> n + add);
val add = 100
val r1 = func(n => n + add)
クロージャーの外の変数から所有権を移動する
let my_struct = Arc::new(MyStruct { value: 100 });
let my_struct_clone = my_struct.clone();
let r = func(move |n| n + my_struct_clone.value);
   

クロージャーのdynとimpl

クロージャーを定義するとき、実はその型にはdynとimplの2種類がある。
(dynはたぶんdynamicの略)

  備考
デフォルト
fn func(f: Fn(i32) -> i32) -> i32 {
    f(123)
}
implもdynも付けないとコンパイルエラーになる。[2024-10-13]
(以前は何も付けないとimpl扱いだったような?)
impl
fn func(f: impl Fn(i32) -> i32) -> i32 {
    f(123)
}
let r = func(|n| n + 1);
 
dyn
fn func(f: &dyn Fn(i32) -> i32) -> i32 {
    f(123)
}
let r = func(&|n| n + 1);
dynにするときは参照で受け渡す。
ジェネリクス
// 型引数にクロージャーの型を書く方式
fn func<F: Fn(i32) -> i32>(f: F) -> i32 {
    f(123)
}
ジェネリクスを使うと、implやdynを書かなくてもいい。
関数の引数にクロージャーの型を書くと長ったらしくなってしまうので、ジェネリクスを使って型を別途定義しておくことは よく行われるらしい。
// ジェネリクスのwhereを使った方式
fn func<F>(f: F) -> i32
where
    F: Fn(i32) -> i32,
{
    f(123)
}

クロージャーがコンパイルされるとき、dynはトレイトオブジェクトになり、implはクロージャー用の具体的な型が作られるそうだ。
基本的にはimplを使った方がいいらしい(implの方が高速らしい)が、implのクロージャーは構造体のフィールドに(単純には)保持できない。

構造体のフィールドでクロージャーを保持する方法


Rustへ戻る / 技術クロージャーへ戻る
メールの送信先:ひしだま