W.I.S. Laboratory
menu-bar

Rust


Rustの行末セミコロンはいるのかいらないのか

RustはCやC++などと同様、行末にはセミコロンが基本的に必要だ。
ただ、セミコロンを書いたらエラーになる箇所と、書かなかったらエラーになる箇所と、書いても書かなくてもエラーにはならない箇所がある。
原則的には、下のような感じ。

書いたらエラーになる箇所

  • 戻り値のある関数で return を明示していない最終行
  • 書かなかったらエラーになる箇所

  • let文の終わり
  • 代入文の終わり
  • 戻り値のある関数の最終行ではない行(つまりコードのほとんどの行末)
  • 書いても書かなくてもエラーにならない箇所

  • 戻り値の無い関数の最終行
  • 戻り値のある関数で return を明示した最終行
  • Rust では行末セミコロンがある場合と無い場合で意味合いが異なる。
    ある場合は「その値を破棄する」、無い場合は「その値を返す」という意味になるのだが、書いても書かなくてもエラーにならない箇所はどちらにしても何も起こらない。

    また原則的にはブレース閉じ「 } 」の後にセミコロンは不要なのだが、例外的に「 }; 」としなくてはならない箇所がある。
    変数に何かを代入した後には必ずセミコロンを書かなくてはならない。
    例えば構造体やユニオンのインスタンスを変数に代入した場合や、if-elseを式として使い戻り値を変数に代入した場合、クロージャを変数に代入した場合などには必要となる。(letがあってもなくても必要)

    struct Foo {
     name: String,
     age: i32,
     height: i32,
    }

    impl Foo {
     fn get_linkingstr(&self) -> String {
      let mut linkstr = "".to_string();
      let age = self.age.to_string();
      let height = self.height.to_string();
      linkstr.push_str(&self.name);
      linkstr.push_str(" ");
      linkstr.push_str(&age);
      linkstr.push_str(" ");
      linkstr.push_str(&height);

      linkstr // ← この値を返したい(returnしたい)のでセミコロンはいらない
     }
    }

    fn main() {
     let a = 1;
     print!("{} ", a);

     let f: Foo;
     f = Foo {
      name: String::from("Taro"),
      age: 15,
      height: 170,
     }; // ← インスタンスを変数に代入した後なのでセミコロンが必要

     println!("{}", f.get_linkingstr());

     let b = 8;
     let i;
     i = if b % 2 == 0 {
      b * 10
     } else {
      b * 20
     }; // ← if-else式の戻り値を変数に代入した後なのでセミコロンが必要

     println!("{}", i);

     let c;
     c = | x:i32 | { x * 2 }; // ← クロージャを変数に代入した後なのでセミコロンが必要

     println!("{}", c(15));
    }

    他にも例外がいろいろあって「必ずこうだよ」といえない部分も少なからずあるのだが、だいたいこの辺りを押さえておけば混乱することはないと思う。
    セミコロンを書いてはいけないのは、戻り値のある関数の最終行なのだが、return を明示した場合は書いても書かなくてもエラーにはならない。
    return を明示せず行末セミコロン無しのほうが Rust らしい書き方になると思う。
    この関数の最後で評価された値が戻り値になるというのは、Rust に限ったことではなく、CもC++もそういう仕様だ。
    「関数最終行で return を明示したほうがコードが読みやすくバグを減らせる」という理由で慣例的に明示するようになったらしい。
    私がCを始めた頃(ウン十年前)は、return 明示の無い関数を書いている人がまだ大勢いたような記憶がある。
    Rustはその書き方を復活させて「セミコロンのあるなし」で戻り値かどうかをコンパイラが明確に区別できるようにした、という感じがする。

    関数の戻り値よりも混乱しそうなのは、ブレース閉じが「 } 」なのか「 }; 」なのかというところかもしれない。
    値やアドレスを変数に代入した後はセミコロンが必要なので、とりあえずそれだけ意識しておけばエラーは減らせると思う。(ただしその値を代入せず関数の戻り値としたい場合はセミコロンはいらない・・ややこしい)

    どうも「その値を変数に代入しなかったら戻り値として返せる時」はセミコロンが必要らしい。(代入せずにセミコロンを付けなければそのまま戻り値にできる)
    それはブレースに限ったことではなく、リテラルもそうだし、配列やベクタのブラケット、タプルやボックスのパーレンなども同様だ。

    let x = 10;
    let s = "abc";
    let a = [10, 20, 30];
    let v = vec![10, 20, 30];
    let t = (10, "ab", 30);
    let b = Box::new(10);
    let s = S {x:10, y:20, z:30};
    let i = if x>10 {x} else {x*2};
    let c = |x:i32| {x*2};

    まあたしかに配列やタプルにはセミコロンが必要なのに、構造体やユニオンには不要となると、それはそれで整合性がとれない。
    ブレースだけ特別扱いというわけにもいかなかったのだろう。
    つまり代入文のブレースは「fnやtraitなどのスコープとは違って、配列やタプルと同じように要素を囲う記号なんだよ」というのがRustの考え方なのではないか・・という勝手な想像だが、そう考えれば納得がいく。
    いくのだが、他に括弧記号が無かったのだろうとは思いつつもやっぱりややこしい。


    [ 戻る ]
    saluteweb