【Rust】がばがばRust独学 - 3. コメントアウトと制御構文
Rustの公式ドキュメントを通して、Rustを独学していく記事になります。(がば要素あり)
今回は、コメントと制御構造について学びつつ、気になった点をコード確認した結果を記事にしています。
- 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
に入れて回していきます。
iterator
は Some
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
だとわかりました。
また、 rev
は iterator
ともわかりました。コンパイラでわかるのは本当に便利・・・
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周りに関しての理解を深める必要がありそうです。 以上。