- 最近Rustに入門し始めたので、インストールと書き方の基本的なシンタックスについて復習を兼ねてアウトプットしていきます。
- 今までPythonなどを使うことが多かったので、Python等との比較もある程度書いていきます。
- 初心者です。その点はご留意ください。
使うもの
- Windows10
- VS Code
インストール
- [1]. Install Rustにアクセスします。
- [2]. Rustのインストールやバージョン管理などを扱うrustupというものを使っていきます。
- [3]. 今回は64-bitのWindowsなので、「DOWNLOAD RUSTUP-INIT.EXE (64-BIT)」を選択します。
- ※macOSやLinuxをお使いの方は別のコマンドなどの対応となるので、リンク先の説明などに合わせてご対応ください。
- [4]. ダウンロードしたexeファイルをダブルクリックして起動するとコマンドプロンプトが起動してインストールが開始されます。完了してエンターを押すとコマンドプロンプトが閉じるので、別途コマンドプロンプトやPowerShellなどを起動するとrustが使えるようになっています。
- ※場合によっては事前にVisual Studio C++ Build toolsが必要になるようです(私の環境では既にインストール済みだったのか、特に気にせずに進んだので本記事では割愛します)。
インストール完了後、コマンドプロンプトなどのターミナル上でrustupやrustcを利用できるか確認します。ヘルプを表示してみて、認識してくれればOKです。もし認識してくれていない場合、正常にインストールがされていなかったりPATHが通っていないなどの可能性があります(私の場合はインストールでそのままPATHが設定されました)。
$ rustup --help
rustup 1.22.1 (b01adbbc3 2020-07-08)
The Rust toolchain installer
USAGE:
rustup.exe [FLAGS] [+toolchain] <SUBCOMMAND>
...
rustcはRustのコードをコンパイルするために必要になります。rustupのインストールで同時に使えるようになります。
$ rustc --help
Usage: rustc [OPTIONS] INPUT
Options:
-h, --help Display this message
--cfg SPEC Configure the compilation environment
...
cargoの概要と確認
cargoはRustのプロジェクトやライブラリの管理などを扱うツールです。前述のrustupでインストールした場合最初から入っています。Pythonで言うとpipやpoetryライブラリみたいな部分を担当します。
以下のコマンドでcargoが入っていることやバージョンを確認することができます。
$ cargo --version
cargo 1.48.0 (65cbdd2dc 2020-10-14)
cargoでプロジェクトを作る
$ cargo new <プロジェクト名>
というコマンドで新しいRustプロジェクトを作ることができます。
コマンドを実行したディレクトリの直下にプロジェクト名で指定した名前のフォルダが作成されるので、プロジェクトフォルダを作成したいディレクトリに事前にcdコマンドなどで移動しておきます。
今回はテックブログ記事執筆用のプロジェクトなので、blog_rust
という名前のプロジェクトにしました。
$ cargo new blog_rust
コンパイルなどは該当のプロジェクトフォルダ内に移動しておかないといけないので移動しておきます。
$ cd blog_rust
$ dir
もしくは$ ls
コマンドで内容を確認すると、以下のようなものが生成されていることが確認できます。
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 2020/12/10 8:53 src
d----- 2020/12/10 8:54 target
-a---- 2020/12/10 8:54 140 Cargo.lock
-a---- 2020/12/10 8:53 222 Cargo.toml
※target
フォルダやCargo.lock
ファイルは最初は無いかもしれません。プロジェクトをコンパイルなどすると出現します。
- srcはプロジェクトのソースコードが配置されます。プロジェクト作成直後では
main.rs
という、コードのエントリーポイントとなるファイルのみ生成されています。 - targetはコンパイル結果のバイナリや実行ファイルなどが格納されるフォルダです。
- Cargo.lockはプロジェクトのソースバージョンやライブラリのバージョンなどの制御に使われます。
- Cargo.tomlは利用されるRustバージョンや依存ライブラリのバージョン指定などに使います。Pythonで言えばrequirements.txtやpoetryで生成されるdependenciesの指定用のファイルなどに該当します。
main.rs の中を確認してみる
cargo new
コマンドによる新しいプロジェクトの作成後に追加されているsrc/main.rs
のファイルを覗いてみます(Rustのコードの拡張子は.rs
になります)。
fn main() {
println!("Hello, world!");
}
中身はとてもシンプルで、Hello, world!の記述が確認できます。
fnは関数定義のキーワードで、Pythonのdefやjsのfunctionに該当します。
main関数はコードのエントリーポイント(ビルド後のものを実行した際に最初に実行される関数)となります。
println!はコンソール出力用の関数(マクロ)です。Pythonでいうところのprint関数、jsで言うところのconsole.logなどに該当します。Rustのマクロ関係はまだそこまで勉強できていないので、勉強が進んだら将来機会があれば別の記事で触れようと思います。
RustのコードをビルドしてHello, world!してみる
早速main.rsのコードをビルドしてRustでHello Worldしてみましょう。
ビルドやシンタックスチェックをするには以下のコマンドの選択肢があります。
-
$ cargo build
コマンド : プロジェクトのビルドを行います。コードの実行は行われません。 -
$ cargo run
コマンド : プロジェクトのビルドを行い、且つコードの実行も行います。 -
$ cargo check
コマンド : ビルドができるかチェックがされます。ビルドやコードの実行自体はされません。3つの中で一番速く終わるので、定期的なコードチェックなどに向いています。
今回はコードのビルドとmain.rs
の実行を行いたいため、$ cargo run
コマンドを使います。
コマンドはプロジェクトフォルダ直下(Cargo.tomlなどがあるディレクトリ)で実行する必要があります。
$ cargo run
Compiling blog_rust v0.1.0 ...
Finished dev [unoptimized + debuginfo] target(s) in 0.82s
Running `target\debug\blog_rust.exe`
Hello, world!
実行してみると上記のようなメッセージが出ます。コンパイルエラーなどがあるとここでエラーメッセージが表示されます。
ビルドが終わるまでの時間や、ビルドで生成されたexeファイルが実行されていることが分かります(LinuxやmacOSなどだとexe以外になります)。
最後にプログラムが実行され、Hello, world!が出力されていることが確認できます。
VS Codeの拡張機能の設定
Rustのコードを書き始める前に、VS Codeを使っているのですが拡張機能に関して事前に入れておくと便利なので少し触れておきます。
拡張機能はとりあえずRust自体の拡張機能とTabnineの2つを入れました。
Tabnineの方はRust以外も含めた機械学習による補完がされます。行単位での補完や通常は補完の効きづらい箇所の補完、その他文字列などの補完なども効いて便利です。Pythonとかだと似たようなものでKiteなどがあります。
なお、他の言語でも補完が効くのでお仕事のPythonでも使っています。とても快適です。
Rust勉強していて知って、Pythonのお仕事でも数日前から使い始めたんだけどTabnineっていとう補完のVS Code拡張中々良いよ!
— simonritchie (@simonritchie_sd) November 30, 2020
Kite的な機械学習の補完は最初は結構戸惑うけど慣れたら楽。docstringなどまで含めて補完してくれる。
最近はPylance・Kite・Tabnine・AllAutocompleteで入力補完やってる。 pic.twitter.com/xxoNf0noMh
(該当のツイート誤字がありますね・・・;)
Rustの基本文法
無事RustでHello worldが出来たので、具体的なRustの基礎文法について次の節から学んでいきます。
変数宣言
Rustの変数はletで宣言します。
fn main() {
let x = 10;
}
変数の内容のコンソール出力
変数の内容をコンソール出力したい場合には、println!の第一引数の文字列内に{}
の括弧を記述し、第二引数以降にその変数を指定すると出力することができます。
fn main() {
let x = 10;
println!("xの値は{}です。", x);
}
コードを実行して確認してみます。
$ cargo run
xの値は10です。
複数の変数を出力したい場合は{}
の括弧を複数文字列中に設定し、第三引数以降に順番に指定していくと対応ができます。
fn main() {
let x = 10;
let y = 20;
println!("xの値は{}、yの値は{}です。", x, y);
}
xの値は10、yの値は20です。
変数のデフォルトはイミュータブル
最初びっくりしたのですが、Rustの変数はデフォルトだとイミュータブル(immutable: 不変)です。つまり再代入ができません。
以下のように再代入しようとすると怒られます。
fn main() {
let x = 10;
x = 20;
}
error[E0384]: cannot assign twice to immutable variable `x`
--> src\main.rs:3:5
|
2 | let x = 10;
| -
| |
| first assignment to `x`
| help: make this binding mutable: `mut x`
3 | x = 20;
| ^^^^^^ cannot assign twice to immutable variable
error: aborting due to previous error; 2 warnings emitted
For more information about this error, try `rustc --explain E0384`.
ミュータブルにする(変数の値を変更可能にする)場合にはmutキーワードを追加する必要があります。
fn main() {
let mut x = 10;
x = 20;
println!("xの値は{}です。", x);
}
xの値は20です。
整数の値はアンダースコア込みで書ける
Pythonにも同じように書けるようになっていますが、整数の値にアンダースコアを含めることができます。これによって、KやMなどの単位での1000とか100万とかの桁が分かりやすくなります。
コンパイルして動かした時にはアンダースコア無しの状態で扱われます。
fn main() {
let x = 10_000_000;
println!("xの値は{}です。", x);
}
xの値は10000000です。
同名の変数の再宣言 : シャドーイング
Rustでは同じスコープ内で、同じ名前の変数をletキーワードを複数回使うことで再宣言することができます。ミュータブルにするためのmutキーワードなども省略できます。
fn main() {
let x = 100;
let x = 200;
println!("xの値は{}です。", x);
}
このように同じスコープ中で同名の変数宣言を複数回することをRustではシャドーイング(Shadowing)と呼ぶようです。
シャドーイングを使うと、途中から変数の型を変えても同じ名前を利用することができます。
例えばユーザーに数字の入力を求めるようなケースで、最初は文字列ですが後で整数にキャストする・・・みたいなケースで変数名を変えなくて済むので便利なケースがあります。
fn main() {
let user_input = "100";
let user_input: i32 = user_input.parse()
.expect("整数変換に失敗しました。");
println!("入力された整数は{}です。", user_input);
}
入力された整数は100です。
※parseは文字列を指定された型(今回はi32で整数)へ変換するメソッド、expectはエラーハンドリング的な必要になる処理となります。
定数の利用
RustにはPythonと異なり、定数定義が存在します(Pythonも新しいバージョンでは型アノテーションのFinalで同じようなことはできるようになっていますが・・・)。
定数を定義するにはconstキーワードを使います。定数の場合型のアノテーション(i32
など)が必須になります(型に関しては後々の節で触れます)。また、定数名は他の言語と同じように大文字 + 単語はアンダースコア区切りで扱うのがRustでも一般的なようです。
fn main() {
const X_VALUE: i32 = 100;
println!("定数の値は{}です。", X_VALUE);
}
定数の値は100です。
もちろん定数に対して再代入などをしようとすると怒られます。
fn main() {
const X_VALUE: i32 = 100;
X_VALUE = 200;
println!("定数の値は{}です。", X_VALUE);
}
error[E0070]: invalid left-hand side of assignment
--> src\main.rs:3:13
|
3 | X_VALUE = 200;
| ------- ^
| |
| cannot assign to this expression
error: aborting due to previous error
For more information about this error, try `rustc --explain E0070`.
Rustでの変数などへの型の指定
Rustで変数などに型を指定したい場合にはlet <変数名>: <型名>
といったように書きます。letの部分は定数であればconstになるといったようにそれぞれで変わります。
例えば32bitの整数の型(i32
)であれば以下のように書きます。
fn main() {
let int_val: i32 = 100;
println!("整数の値は{}です。", int_val);
}
なお、Rustでは多くのケースでは型を指定せずとも型が推論されてビルドが通ります。上記のコードであれば: i32
部分を省略してもRust側で該当の変数は整数型だと判定されてビルドされます(型を明示しない場合、整数では32bitの整数になります)。
fn main() {
let int_val = 100;
println!("整数の値は{}です。", int_val);
}
エディタ上でマウスオーバーしてもi32
として認識してくれていることが確認できます。
ただし定数などは型を明示しないといけなかったり、関数やメソッドの返却値が複数の型をとりうる場合などには型を明示しないとビルドエラーになるケースも結構あるようです。
Rustの整数の型
Rustには各bit数の整数の型がそれぞれ存在します。また、正と負の両方の値を取れる型(integer, int)と正の値のみ取れる型(unsigned integer, uint)が存在します。
intの場合であればi<ビット数>
のように書きます。例えば32-bitのintであればi32
といった型の指定になります。
uintであればu<ビット数>
のように書きます。32-bitであればu32
といった具合です。
ビット数はintとuintでそれぞれ8, 16, 32, 64, 128が存在します。
Python3系だとビルトインの整数は値に応じてビット数が自動で変動する(途中で大きな値になったら大きいビット数が割り振られる)ため、全てintのみになっているのであまりビット数は普段考えなくて済む実装になっています。NumPyなどだとint64とかuint8とかはありますね。
ビット数の大きい型の方が扱える最大の整数の値が大きくなりますが、その分メモリ等の負荷は大きくなります。
他にもOSのアーキテクチャに依存する型の指定も存在します。例えば32bitのOSであれば32bitの整数、64bitのOSであれば64bitの整数といった具合です(といいつつ、32bitのPCが多かったのが15年くらい前?な気がしないでもないのでもうあまり使う機会はないかも・・・という気はしています)。
上記のようにOS依存にしたい場合にはintの整数であればisize
、uintの整数であればusize
という型を指定します。
fn main() {
let int_val: isize = 100;
println!("整数の値は{}です。", int_val);
}
2進数・8進数・16進数の書き方
0と1のみの2進数(Binary number)で数字を表現したい場合には数字の前に0b
と付けます。たとえば11110000(10進数の240)を表現したい場合には0b11110000と書きます。0b
を使った書き方はPythonと同じですね。
コンソール出力などをしてみると、値は10進数の整数になって扱われることが確認できます。
fn main() {
let binary_value = 0b11110000;
println!("値は{}です。", binary_value);
}
値は240です。
8進数(Octal number)の場合には数字の前に0o
と付けます(ただしゲーム関係とweb関係のお仕事をしてきて、今のところ8進数は使ったことがない・・・)。
0o
と付ける書き方もPythonと同じです。
fn main() {
let octal_value = 0o764;
println!("値は{}です。", octal_value);
}
値は500です。
カラーコードなどでお馴染みの16進数(Hexadecimal number)は数字の前に0x
と付けます。こちらもPythonと同じですね。
fn main() {
let hex_value = 0xff0000;
println!("値は{}です。", hex_value);
}
値は16711680です。
Rustの浮動小数点数の型
小数点以下の値を含む値、いわゆるfloatの型(Floating-Point type)について触れていきます。
Rustでは32bitのfloatと64bitのfloatの型が存在します。それぞれf32
もしくはf64
という型の指定になります。
以下のように型の指定を省略した場合にはデフォルトでは64bit(f64
)の型となります。
fn main() {
let float_val = 3.14;
println!("浮動小数点数の値は{}です。", float_val);
}
32bitの型を使いたい場合には以下のように型を明示する必要があります。
fn main() {
let float_val: f32 = 3.14;
println!("浮動小数点数の値は{}です。", float_val);
}
Rustの真偽値の型
Rustの真偽値の型はbool
です。型名はPythonと一緒ですね。
真と偽の値はそれぞれtrue
とfalse
となります。PythonだとシングルトンとしてTrue
とFalse
といった具合に最初が大文字になっていますがRustでは小文字です。
fn main() {
// 型の指定を省略する場合。
let true_val = true;
// 型を明示する場合。
let false_val: bool = false;
println!("{}, {}", true_val, false_val);
}
true, false
Rustの文字の型
Rustでは文字(char)と文字列(&str or String)は別物です。
また、Rustではシングルクォートとダブルクォートで挙動が異なります。PythonだとPEP8とかでプロジェクトでどちらのクォートを使うのか決めてそれに合わせる的な記述がありますが、Rustでは用途が明確に分かれているので他の言語のようにシングルクォートやダブルクォートどちらを使えばいいか迷ったりしないのはいいですね。
文字単体(character)の値の型はchar
となります。また、ダブルクォートではなくシングルクォートで囲う必要があります。
fn main() {
let char_val: char = 'a';
println!("文字の値は{}です。", char_val);
}
char
型を指定した状態で文字列(複数の文字)を指定するとエラーになります。
fn main() {
let char_val: char = 'apple';
println!("文字の値は{}です。", char_val);
}
error: character literal may only contain one codepoint
--> src\main.rs:2:26
|
2 | let char_val: char = 'apple';
| ^^^^^^^
|
help: if you meant to write a `str` literal, use double quotes
|
2 | let char_val: char = "apple";
| ^^^^^^^
error: aborting due to previous error
なお、1文字であれば問題はないので日本語や絵文字などでもchar型に格納することができます。
fn main() {
let char_val: char = '😎';
println!("文字の値は{}です。", char_val);
}
Rustでの文字列の型
Rustの文字列では&str
とString
の2つの型があるそうです。それぞれについては結構深い話なようで、長くなるのでここでは触れません。詳しくは以下の記事をご参照ください。
[Rust] &strとStringを理解しようと思ったらsliceやmutを理解できてないことに気づいた話
文字列にはダブルクォートを使います。
fn main() {
let string_val = "apple";
println!("文字列の値は{}です。", string_val);
}
文字列の値はappleです。
Rustのタプルの型
RustのタプルはPythonのタプルとほぼ同じような書き方で、()
の括弧を使うのと値はコンマ区切りになります。
型の指定に関しては()
の括弧を使って値の件数分個別の型を指定します。例えばintとfloatの2つの値を格納するような型の場合は: (i32, f64)
といった書き方になります。
fn main() {
let tpl: (i32, f64) = (100, 3.14);
}
Python(3.9以降)だと型は以下のように書きます。少し書き方が異なりますが、値の数だけ個別の型を入れる形は近い記述になります。
tpl: tuple[int, float] = (100, 3.14)
タプル内の値にアクセスするには.<インデックス番号>
といったように書きます。例えば先頭の値であれば.0
といったように書きます。括弧などは不要です。
fn main() {
let tpl: (i32, f64) = (100, 3.14);
println!("タプルの最初の値は{}です。", tpl.0);
println!("タプルの2番目の値は{}です。", tpl.1);
}
タプルの最初の値は100です。
タプルの2番目の値は3.14です。
最初誤解していたのですが、Rustのタプルはmut(ミュータブル)キーワードを付けると普通に編集できるようになるようです。Pythonのようにタプルがイミュータブルになったりはしません。
fn main() {
let mut tpl: (i32, f64) = (100, 3.14);
tpl.0 = 200;
println!("タプルの最初の値は{}です。", tpl.0);
}
タプルの最初の値は200です。
Rustの配列の型
配列(Array)の書き方もPythonなどと大体一緒で、[]
の括弧を使います。値へのアクセスも[<インデックス番号>]
といったように[]
の括弧を使います。最初のインデックスであれば[0]
といった具合です。
fn main() {
let arr = [10, 20, 30];
println!("配列の最初の値は{}です。", arr[0]);
}
配列の最初の値は10です。
注意すべき点として、Rustの配列は固定長です。つまり値の追加などができないようです。
値の追加ができるものは別途ベクター型というものが存在します。Pythonのリストなどはどちらかというとこのベクター型の方が近いイメージになります。ベクター型に関しては別途機会があれば別の記事で書きます。
配列のインデックス範囲を超えるインデックスを指定した場合にはエラーになります。直接値を指定した場合にはビルドの時点でエラーで弾かれます。
fn main() {
let arr = [10, 20, 30];
println!("配列の四番目の値は{}です。", arr[3]);
}
error: this operation will panic at runtime
--> src\main.rs:3:32
|
3 | println!("配列の四番目の値は{}です。", arr[3]);
| ^^^^^^ index out of bounds: the length is 3 but the index is 3
|
= note: `#[deny(unconditional_panic)]` on by default
error: aborting due to previous error
配列の長さを取得したい場合には、Pythonではlen関数を挟む形ですがRustでは配列自体がメソッドとしてlenを持っています。
fn main() {
let arr = [10, 20, 30];
println!("配列の長さは{}です。", arr.len());
}
配列の長さは3です。
数値の四則演算と剰余
Rustの四則演算と剰余で必要となる記号はPythonと変わらず、+-*/%
です。
加算(addition)例:
fn main() {
let addition_result = 10 + 5;
println!("値は{}です。", addition_result);
}
値は15です。
減算(subtraction)例:
fn main() {
let subtraction_result = 20 - 10;
println!("値は{}です。", subtraction_result);
}
値は10です。
乗算(multiplication)例:
fn main() {
let multiplication_result = 20 * 3;
println!("値は{}です。", multiplication_result);
}
値は60です。
除算(division)例:
fn main() {
let division_result = 100 / 15;
println!("値は{}です。", division_result);
}
値は6です。
除算をしていて気づきましたが、型推論などで処理する場合でもどうやら割り切れない場合は切り捨てになって整数になっている感じがあります。この辺りはPython2系に近い挙動でしょうか。そのままだとPython3系のように除算結果がfloatになったりはしないようです。
試しに計算自体に.0
を付けてfloatの値同士で計算してみると、結果もfloatの結果になりました。
fn main() {
let division_result = 100.0 / 15.0;
println!("値は{}です。", division_result);
}
値は6.666666666666667です。
変数の型自体をf64などにしておけば整数同士の除算結果もfloatになるのでは?と試してみますがそちらはビルドエラーになります。結果の値をf64にしたければ除算の数値側もそれぞれfloatにする必要があるようです。
fn main() {
let division_result: f64 = 100 / 15;
println!("値は{}です。", division_result);
}
error[E0308]: mismatched types
--> src\main.rs:2:32
|
2 | let division_result: f64 = 100 / 15;
| --- ^^^^^^^^ expected `f64`, found integer
| |
| expected due to this
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
このあたりはPythonと比べると大分型の厳密性が高いですね・・・。ただ、仕事でPythonプライベートでRust・・・としていると、稀にfloatになる感覚で除算の処理を書いてミスしそうな気はします。気を付けます。
最後に剰余です。
剰余(remainder)例:
fn main() {
let remainder_result = 100 % 15;
println!("値は{}です。", remainder_result);
}
値は10です。
Rustでの関数の書き方
既にこれまででエントリーポイントとなるmain関数は使ってきましたが、この節からは関数に関して詳しく触れていきます。
関数宣言のキーワードはfnです。Pythonのdefに該当します。
引数設定部分は()
の括弧を使い、複数の引数の場合にはコンマ区切りで設定します。この点はPythonと一緒ですね。
関数内容の開始と終了は{}
の括弧を使います。Pythonだとこの括弧は無くインデントで表現となるので、この辺はjsとかに近いイメージです。
関数の呼び出し
他の関数から特定の関数を呼び出したい場合には関数名()
といったように()
の括弧を関数名の直後に付けます。
fn main() {
print_hello();
}
fn print_hello() {
println!("Hello Rust!");
}
Hello Rust!
引数設定
引数は()
括弧の中に記述します。複数の引数の場合にはコンマで区切ります。型アノテーションも必須になります。
fn main() {
print_info("タマ", 5);
}
fn print_info(name: &str, age: i32) {
println!("名前は{}、歳は{}歳です。", name, age);
}
名前はタマ、歳は5歳です。
返却値設定
返却値を指定したい場合には関数の引数設定と{
の括弧の前に-> <型名> {
といったような型アノテーションの書き方が必要になります。
fn main() {
let age = get_age();
println!("歳は{}歳です。", age);
}
fn get_age() -> i32 {
return 5;
}
Pythonだと型アノテーション付きだと以下のように書くので、型の記述関係はある程度似ています。
def get_age() -> int:
return 5
なお、Rustでは関数の末端の部分の場合returnキーワード無しに返却値を設定することができます。例えば以下のような書き方ができます。
fn main() {
let age = get_age();
println!("歳は{}歳です。", age);
}
fn get_age() -> i32 {
5
}
歳は5歳です。
この場合値の後にセミコロンは付けていない点には注意してください。セミコロンを付けるとエラーになります。
fn main() {
let age = get_age();
println!("歳は{}歳です。", age);
}
fn get_age() -> i32 {
5;
}
error[E0308]: mismatched types
--> src\main.rs:6:17
|
6 | fn get_age() -> i32 {
| ------- ^^^ expected `i32`, found `()`
| |
| implicitly returns `()` as its body has no tail or `return` expression
7 | 5;
| - help: consider removing this semicolon
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
この辺りはRustの式(expression)と文(statement)による挙動による影響となり、セミコロンが付いた場合にはstatementとなり値を返さない形となり、セミコロンが無い場合には値が返却される(変数などに代入ができる)という仕様に依存しています。
expressionとstatementに関してはそれだけで記事がかなり長くなりそうなので今回は割愛します。以下の記事がとても参考になりました。
関数の途中でif文などで返却値を設定したい場合にはreturnキーワードが必要です。
Rustのコメント
インラインコメントはjsのように//
で書きます。/**/
などによる複数行を対象としたブロックコメントは無く、複数行コメントアウトが必要な場合は各行に//
の記述が必要になります(まあPythonでもブロックコメント無くても特に困っていないので問題は無いでしょう…)。
fn main() {
// コードの上にコメントを書くことができます。
let age = 5; // 行末にコメントを置くことも可能です。
}
Rustの条件分岐
条件分岐にはif
、else if
、else
といったキーワードを使います。Pythonのelif
がelse if
となる以外はキーワードの挙動は一緒です。
条件文のところに()
の括弧が不要なのはPythonと一緒ですが、条件に該当する処理のブロックには{}
の括弧が必要になります。
fn main() {
let num = 10;
if num > 7 {
println!("数値は7よりも大きな値です。");
} else if num > 5 {
println!("数値は5よりも大きな値です。");
} else {
println!("数値は5以下の値です。");
}
}
なお、Rustのif文の条件には真偽値しか許容されません。つまり他の言語でよくあるような数値などを条件に指定することができません(0だったらfalse, nanやNoneであればfalse, 空文字だったらfalse...といった制御はできません)。そのためjsやPHPなどである厳密等価演算子(===
)なども存在しません(個人的には等価演算子が1つだけで、且つ厳密でミスが減りそうなので好みな仕様です)。
以下のように数値などを条件に指定するとエラーになります。
fn main() {
let num = 0;
if num {
println!("条件を満たしています。");
}
}
--> src\main.rs:3:8
|
3 | if num {
| ^^^ expected `bool`, found integer
error: aborting due to previous error
For more information about this error, try `rustc --explain E0308`.
Rustのループ制御
Rustのループはloop
, while
, for
の三つがあります。while
はPythonとほぼ一緒、for
もPythonに近いものになります。loop
はPythonには無いものですが、breakさせるまでずっとループし続ける・・・といったキーワードになります(条件文を切り離して書く必要のあるwhile文・・・といった挙動をします)。
fn main() {
let mut num = 0;
loop {
if num == 10 {
break;
}
num += 1;
}
println!("ループ後の値は{}です。", num);
}
ループ後の値は10です。
loopとwhileは多少書き方が変わるもののお互いに似たような制御と挙動になります。
fn main() {
let mut num = 0;
while num != 10 {
num += 1;
}
println!("ループ後の値は{}です。", num);
}
ループ後の値は10です。
forに関しては配列などのiterメソッドと組み合わせることで、Pythonのforと同じような挙動をします。
fn main() {
let num_arr = [10, 20, 30];
for num in num_arr.iter() {
println!("配列の値は{}です。", num);
}
}
配列の値は10です。
配列の値は20です。
配列の値は30です。
Pythonのrange(10)
的な、特定の整数範囲でループを回したい場合には<開始インデックス>..<終了インデックス + 1>
みたいな書き方になります。例えば0~9の範囲でループを回したい場合には0..10
といった書き方になります。Python同様終了側は含まないため1大きい値が必要になります。
fn main() {
for num in 0..10 {
println!("値は{}です。", num);
}
}
値は0です。
値は1です。
...
値は8です。
値は9です。