はじめに
この記事では、Rustのクロージャに関する奇抜な(JavaScriptのパラダイムに似た)応用を紹介していきます。
Rustらしくない書き方だと思うので、こんなこともできるんだ程度に思ってもらえれば幸いです!
Rustにおけるクロージャ
Rustのクロージャは、 || -> i64 { 1 }
のように書くことができる非常にシンプルな関数です。
let x = |i: i64| -> i64 { i };
println!("{}", x(1));
どの部分がどこにあたるかは下記の通りになります。
let 関数 = |引数| -> 戻り値 { 返却する値 };
即時関数
クロージャを応用して、即時関数を作ることも可能です。
即時関数はクロージャを変数に代入することなく即実行されます。
println!("{}", (|| -> &str { "1" })());
即時関数でResultをうまく扱う
即時関数によって、戻り値を指定することが可能なので ?
や try!
を使ってエラーが発生した場合に即 Err<String>
を返すような処理を簡潔に書くことが出来ます。
(|| -> Result<(), String> {
let mut file_s = File::open("s.txt").map_err(|err| err.to_string())?;
let mut contents = String::new();
file_s.read_to_string(&mut contents).map_err(|err| err.to_string())?;
let mut file_w = File::open("w.txt").map_err(|err| err.to_string())?;
let mut contents = String::new();
file_w.read_to_string(&mut contents).map_err(|err| err.to_string())?;
let mut file_s = File::create("s.txt").map_err(|err| err.to_string())?;;
let mut file_w = File::create("w.txt").map_err(|err| err.to_string())?;
file_s.write_all(b"").map_err(|err| err.to_string())?;
file_w.write_all(b"").map_err(|err| err.to_string())?;
Ok(())
})();
即時関数でOptionをうまく扱う
Optionに対して ?
を使うと、Someの中身を展開してくれる仕組みをクロージャ内に閉じ込めて使うことが出来ます。
(|| -> Option<&str> {
let x = Some("1")?;
println!("{}", x);
let x = Some(x)?;
println!("{}", x);
let x = Some(x)?;
println!("{}", x);
let x = Some(x)?;
println!("{}", x);
Some(x)
})();
戻り値を省略する
クロージャは戻り値を省略することが出来るため、一切型を書かずにプログラミングすることが出来ます。
let x = (|i: i64| i.to_string());
let y = (|| x(123) + &x(456))();
println!("{}", y);
move クロージャ
moveクロージャの場合変数が移動する際に、変数がCopyを実装している場合コピーされた変数の所有権を得るので、クロージャを抜けると値は5のままになっています。
let mut a = 5;
let mut b = move || a += 5;
b();
println!("{}", a); //=> 5
moveがない場合は、通常の移動となるため数が増えます。
そして同じスコープでmutable borrowが発生しているので、ブロックスコープを外すとエラーになります。
let mut a = 5;
{
let mut b = || a += 5;
b();
}
println!("{}", a); //=> 10
最後に
Rustにはブロックスコープがあるので、あまり即時関数の出番はないかもしれませんが、こんなことも出来ますっていう紹介でした。