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の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のクロージャーは構造体のフィールドに(単純には)保持できない。