Charaken 技術系ブログ

技術系に関して学んだことや、気になったことを記事にしていく。

【Rust】がばがばRust独学 - 3. コメントアウトと制御構文

f:id:charaken:20191223210559p:plain

Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)

doc.rust-lang.org

今回は、コメントと制御構造について学びつつ、気になった点をコード確認した結果を記事にしています。

  • versions
$ rustc --version
rustc 1.40.0 (73528e339 2019-12-16)


コメントアウト

// によってコメントアウトが可能です。

// Hello, world

単一行でコメントを書くことも可能ですが、複数行で書くことを推奨されているようです。

fn main() {
    let lucky_number = 7; // I’m feeling lucky today
    println!("Luck Number -> {}", lucky_number);
}
fn main() {
    // I’m feeling lucky today
    let lucky_number = 7;
    println!("Luck Number -> {}", lucky_number);
}

他言語でもある、 /**/ によるコメントアウトですが、docコメントとして使用することができるようで、 通常変数の場合は、「unused doc comment」となります。

fn main() {
    /**
    * I’m feeling lucky today
    */
    let lucky_number = 7;
    println!("Luck Number -> {}", lucky_number);
}
warning: unused doc comment
 --> demo.rs:2:2
  |
2 |       /**
  |  _____^
3 | |      * I’m feeling lucky today
4 | |      */
  | |_______^
5 |       let lucky_number = 7;
  |       --------------------- rustdoc does not generate documentation for statements
  |
  = note: `#[warn(unused_doc_comments)]` on by default

Luck Number -> 7

docコメントで利用した場合はwarningになりません。

/**
 * I’m feeling lucky today
 */
fn main() {
    let lucky_number = 7;
    println!("Luck Number -> {}", lucky_number);
}
Luck Number -> 7

しかしながら、Chapter 14に記載されていますが、 /// 利用が一般的なようです。

※ 今後、別記事でdoc comment周りはまとめていきます。


制御構造

if, loop, while, for に関してまとめます。 match については、公式ドキュメントのChapter 6で紹介されるということで、今回は除外します。

if

if の後に、制御対象(boolean)を記載することで可能です。

fn main() {
    let x = 5;
    if x > 3 {
        println!("result true");
    } else {
        println!("result false");
    }
}
result true

他言語と同様に AND(&&) や OR(||)も利用可能です。

fn main() {
    let x = 5;
    let y = 6;
    // AND
    if x > 3 && y > 10 {
        println!("result true");
    } else {
        println!("result false");
    }
    // OR
    if x > 3 || y > 10 {
        println!("result true");
    } else {
        println!("result false");
    }
}

演算子系は、公式ドキュメント(B - 演算子とシンボル - The Rust Programming Language)に記載があります。

あと、三項演算子x ? y : z )はifは式であるため無いようです・・・ちょっと辛い。

もちろん、boolean ではない場合は、エラーとなります。

fn main() {
    let x = 6;

    if x {
        println!("Example");
    }
}
error[E0308]: mismatched types
 --> demo.rs:4:5
  |
4 |     if x {
  |        ^ expected bool, found integer
  |
  = note: expected type `bool`
             found type `{integer}

else if も利用可能です。

fn main() {
    let x = 6;

    if x % 4 == 0 {
        println!("x is divisible by 4");
    } else if x % 3 == 0 {
        println!("x is divisible by 3");
    } else if x % 2 == 0 {
        println!("x is divisible by 2");
    } else {
        println!("x is not divisible by 4, 3, or 2");
    }
}
x is divisible by 3

また、if は式であるため、下記のように返り値を持たせて使用することも可能です。

fn main() {
    let x = 6;
    let y = if x > 3 { 10 } else { 9 };

    println!("y -> {}", y);
}
y -> 10

else 無しは、条件に一致しない場合、ifは () を返すため、型不一致でエラーとなります。

fn main() {
    let x = 6;
    let y = if x > 3 {
        10
    };

    println!("y -> {}", y);
}
error[E0317]: if may be missing an else clause
 --> demo.rs:3:10
  |
3 |       let y = if x > 3 {
  |  _____________^
4 | |         10
  | |         -- found here
5 | |     };
  | |_____^ expected (), found integer
  |
  = note: expected type `()`
             found type `{integer}`
  = note: `if` expressions without `else` evaluate to `()`
  = help: consider adding an `else` block that evaluates to the expected type

型不一致はNGなので、下記の例もエラーとなります。

fn main() {
    let x = 6;
    let y = if x > 3 { 10 } else { "10" };

    println!("y -> {}", y);
}
error[E0308]: if and else have incompatible types
 --> demo.rs:3:33
  |
3 |     let y = if x > 3 { 10 } else { "10" };
  |                        --          ^^^^ expected integer, found &str
  |                        |
  |                        expected because of this
  |
  = note: expected type `{integer}`
             found type `&str`
fn main() {
    let x = 6;
    let y = if x > 3 { 10 } else { 1.0 };

    println!("y -> {}", y);
}
error[E0308]: if and else have incompatible types
 --> demo.rs:3:33
  |
3 |     let y = if x > 3 { 10 } else { 1.0 };
  |                        --          ^^^ expected integer, found floating-point number
  |                        |
  |                        expected because of this
  |
  = note: expected type `{integer}`
             found type `{float}`

loop

loop は無限ループであるため、下記のように書くと、ひたすらに again! を出力します。 ※ VSCodeでコード単体デバッグした際に、止められず焦ったのは秘密です。

fn main() {
    loop {
        println!("again!");
    }
}

ループから抜け出すには break を利用し、loop は式であるため、後続する値を返します。

fn main() {
    let mut counter = 0;

    let result = loop {
        counter += 1;

        if counter == 10 {
            break counter * 2;
        }
    };

    println!("The result is {}", result);
}
The result is 20

while

一般的なwhiteと同様に条件に合致している間はループする処理になります。

fn main() {
    let mut number = 2;
    while number != 0 {
        println!("{}!", number);
        number -= 1;
    }
    println!("END");
}
2!
1!
END

for

配列の場合、 iterator (Returns an iterator over the slice.)により、配列要素を一つずつ呼び出し、 x に入れて回していきます。 iteratorSome or None であるため、 for 自体は None が与えられるまで回し続けるようです。

fn main() {
    let a = [1, 2, 3, 4, 5];

    for x in a.iter() {
        println!("x -> {}", x);
    }
}
x -> 1
x -> 2
x -> 3
x -> 4
x -> 5

よくある、 for (i = 0; i < 10; i++) のような形式は無く、 Range を利用します。 Range(1..4) のように表現し、 rev() で取り出すように見受けられます。

fn main() {
    for x in (1..4).rev() {
        println!("x -> {}", x);
    }
}
x -> 3
x -> 2
x -> 1
Range に関して

(1..4) ってなんだ? rev() ってなんだ?と凄く気になるので、分解してみます。

単純に切り出すだけだと、カッコが不要だと怒られます。

unnecessary parentheses around assigned value

fn main() {
    let y = (1..4);
    for x in y.rev() {
        println!("x -> {}", x);
    }
}
warning: unnecessary parentheses around assigned value
 --> demo.rs:2:10
  |
2 |     let y = (1..4);
  |             ^^^^^^ help: remove these parentheses
  |
  = note: `#[warn(unused_parens)]` on by default

x -> 3
x -> 2
x -> 1

カッコを外すと、怒られないため、純粋に rev()1..4 の関数と使用するために、カッコを利用していました。

fn main() {
    let y = 1..4;
    for x in y.rev() {
        println!("x -> {}", x);
    }
}
x -> 3
x -> 2
x -> 1

とりあえず、 1..4 の型を知りたいために i32 を指定すると、型は std::ops::Range だとわかりました。 また、 reviterator ともわかりました。コンパイラでわかるのは本当に便利・・・

fn main() {
    let y: i32 = 1..4;
    for x in y.rev() {
        println!("x -> {}", x);
    }
}
error[E0308]: mismatched types
 --> demo.rs:2:15
  |
2 |     let y: i32 = 1..4;
  |                  ^^^^ expected i32, found struct `std::ops::Range`
  |
  = note: expected type `i32`
             found type `std::ops::Range<{integer}>`

error[E0599]: no method named `rev` found for type `i32` in the current scope
 --> demo.rs:3:13
  |
3 |     for x in y.rev() {
  |                ^^^ method not found in `i32`
  |
  = note: the method `rev` exists but the following trait bounds were not satisfied:
          `&mut i32 : std::iter::Iterator`

error: aborting due to 2 previous errors

んなら、 std::ops::Range を型にしてみようじゃないか

fn main() {
    let y: std::ops::Range = 1..4;
    for x in y.rev() {
        println!("x -> {}", x);
    }
}
error[E0107]: wrong number of type arguments: expected 1, found 0
 --> demo.rs:2:9
  |
2 |     let y: std::ops::Range = 1..4;
  |            ^^^^^^^^^^^^^^^ expected 1 type argument

error: aborting due to previous error

wrong number of type arguments

タイプが足りとらんって怒られる・・・

よくよく、先程の i32 のエラーを見てみると、

found type std::ops::Range<{integer}>

であるからして、配列の中身の型を指定しなければならないようだ。

とりあえず、 i32 辺りで良いので試してみると、実行できた。良かった。

fn main() {
    let y: std::ops::Range<i32> = 1..4;
    for x in y.rev() {
        println!("x -> {}", x);
    }
}
x -> 3
x -> 2
x -> 1

もう一点の疑問、 rev()std::iter::Iterator の関数であるのに、 std::ops::Range で利用できるのだろうか・・・

Rangeのdocument(std::ops::Range - Rust)を確認すると、 impl<A: Step> Iterator for ops::Range<A>range.rs.html -- source) が見受けられるため、RangeはIteratorに化けることができる?

impl周りのドキュメントを読んでみないと、現状はわからない状態です。

こうなると、ScalaのCollectionのように、Seq, Set, Mapへの継承のようなものはあるのだろうか。 ※ 参考 docs.scala-lang.org

最後に

コメントアウトと制御構文に関して、理解が深められました。 今後はコメントアウトについてはdoc生成に関してや、impl周りに関しての理解を深める必要がありそうです。 以上。