2016年2月28日日曜日

C++プログラマー向けRust 翻訳シリーズ2

制御フロー

If


if文は、C++のものと全く一緒だ。一つ違いがあるとしたら、大かっこは必須であり、条件式を囲むかっこはそうではないことだ。
また、Rustのはif式であるため、C++の三項演算子と同じように使用することができる(前回、ブロックの最後の式がセミコロンで閉じていなければ、その値がブロックの戻り値になることを思い出してほしい)。
Rustには三項演算子は存在しない。ゆえに、下記の関数は双方とも同じ挙動をする。

fn foo(x: i32) -> &'static str {
    let mut result: &'static str;
    if x < 10 {
        result = "less than 10";
    } else {
        result = "10 or more";
    }
    return result;
}

fn bar(x: i32) -> &'static str {
    if x < 10 {
        "less than 10"
    } else {
        "10 or more"
    }
}
一番目のコードは、C++のコードの直訳形だ。二番目がRustでは好ましい。

let x = if ...などとすることもできる。

ループ


RustにもまたC++のようにwhileループがある。

fn main() {
    let mut x = 10;
    while x > 0 {
        println!("Current value: {}", x);
        x -= 1;
    }
}
Rustには、do...whileループはないが、単に永久ループするloop文がある。

fn main() {
    loop {
        println!("Just looping");
    }
}
RustにもC++同様、breakcontinueが存在する。

Forループ


Rustにもforループが存在する。しかし、こちらは少し勝手が違う。
整数型のvectorコンテナがあって、すべて表示したいとしよう(vectorコンテナ、配列、イテレータ、およびgenericsについては後ほど詳述する。とりあえず、Vec<T>T型のシーケンスであり、iter関数は順繰りに処理できるものからイテレータを返すものと思っておけばいい)。
シンプルなforループは、以下のような見た目をしている。

fn print_all(all: Vec<i32>) {
    for a in all.iter() {
        println!("{}", a);
    }
}

all変数のインデックスをたどりたい場合(よりC++の配列に作用するforループ風に)、以下のようにも書ける。
fn print_all(all: Vec<i32>) {
    for i in ..all.len() {
        println!("{}: {}", i, all.get(i));
    }
}
len関数が何をするかは容易に想像が付くだろう。

Switch/Match


Rustには、match式がある。これはC++のswitch文に似ているが、ずっと強力だ。
以下のシンプルバージョンは、馴染み深く感じられるだろう。

fn print_some(x: i32) {
    match x {
        0 => println!("x is zero"),
        1 => println!("x is one"),
        10 => println!("x is ten"),
        y => println!("x is something else {}", y),
    }
}
見た目に違いがある。評価された値と実行する式の対応付けに=>という記号を使っているし、match節の区切りに,を使用している(最後の,はあってもなくてもいい)。
さらに意味にもあまり明瞭でない違いがある。評価値は網羅性がなければならない。つまり、評価対象の式(上記の例では変数x)がマッチする可能性のある値すべてが記述されていなければならない。
y => ...の行を削除してどうなるか見てみるといい。そうなるのは、0と1と10にマッチする値しか指定されておらず、未指定の値が数多くあるからだ。最後の節で変数yが評価値(今回のケースでは変数x)に紐づけられている。
次のようにも記述できる。

fn print_some(x: i32) {
    match x {
        x => println!("x is something else {}", x)
    }
}
ここで、match節内の変数xは、引数のxを隠蔽している。これは、内側のスコープで変数を宣言するのと同じだ。

変数に名前をつける必要がなければ、無名変数に_とつけることができる。これは、ワイルドカードマッチを行うのと同じだ。
何もする必要がなければ、空の節を用意すればいい。

fn print_some(x: i32) {
    match x {
        0 => println!("x is zero"),
        1 => println!("x is one"),
        10 => println!("x is ten"),
        _ => {}
    }
}
他にも次のmatch節へのフォールスルーがないという意味の違いもある。

matchは非常に強力だと後ほどの投稿で認識できるだろう。今のところは、もう二つばかり、その機能を紹介しておきたい。or演算子と限定if節だ。
次の例で自明だと嬉しい。

fn print_some_more(x: i32) {
    match x {
        0 | 1 | 10 => println!("x is one of zero, one, or ten"),
        y if y < 20 => println!("x is less than 20, but not zero, one, or ten"),
        y if y == 200 => println!("x is 200 (but this is not very stylish)"),
        _ => {}
    }
}
if式同様、match文も実は式なので、先ほどの例は以下のように書き換えられる。

fn print_some_more(x: i32) {
    let msg = match x {
        0 | 1 | 10 => "x is one of zero, one, or ten",
        y if y < 20 => "x is less than 20, but not zero, one, or ten",
        y if y == 200 => "x is 200 (but this is not very stylish)",
        _ => "something else"
    };

    println!("x is {}", msg);
}
閉じかっこの後のセミコロンに注目してほしい。これは、let文が文であり、let msg = ...;という形にしなければいけないからだ。
右辺にmatch式を代入しているが(match式は通常セミコロンで閉じる必要はない)、let文には必要なのだ。いつも引っかかるんだよな〜。

理由付け:Rustのmatch文では、C++のswitch文でありがちなバグ(breakキーワードを忘れたり、意図なしにフォールスルーすることがない)を回避することができる。また、enum(詳しくは後ほど)に値を追加したら、コンパイラがmatch文に含まれているか確認してくれる。

メソッド呼び出し


最後に、ちょこっとだけRustにもC++のようにメソッドが存在することに注目しよう。Rustのメソッドは必ず.演算子で呼び出す(->演算子はない。これについてはまた別立てで解説する)。すでに数個(len,iter)例が挙がっていた。
定義の仕方や呼び出し方についての詳述は、また将来することにしよう。C++やJava流の推測は概ね当たっているよ。


原文: https://github.com/nrc/r4cppp/blob/master/control%20flow.md

0 件のコメント:

コメントを投稿

なにか意見や感想、質問などがあれば、ご自由にお書きください。