21
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Rustその2Advent Calendar 2017

Day 8

Rustのクロージャを嗜む

Posted at

はじめに

この記事では、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にはブロックスコープがあるので、あまり即時関数の出番はないかもしれませんが、こんなことも出来ますっていう紹介でした。

21
14
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
14

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?