Rustが流行りだしているようなので勉強してみる。
忘れないように、勉強メモを記録する。
OSは Ubuntu (WSLでもよい)
00. 環境構築
以下のコマンドを実行すればコマンドベースのRustコンパイラがインストールできます。
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
環境によっては、Cの開発環境も必要かも。
$ apt install build-essential
練習の準備
練習のために、rust のコードを作成するディレクトリ(フォルダ)を作成します。
$ cd ~
$ mkdir rust
以降、ディレクトリやソースコードは “~/rust”ディレクトリに作成することにします。
01. Hello, world!
はじめてのRustプログラム
まずは、ディレクトリを準備します。
$ cd ~
$ cd rust
$ mkdir hello
$ cd hello
Rustのソースコードの拡張子は .rs です。まずは hello, world! を書いて画面に出力するプログラムを作ります。
fn main() {
println!("Hello, world!!");
}
書いたソースコードをコンパイルします。コンパイラのコマンドは “rustc” です。
“rustc”コマンドによりコンパイルすると、「実行可能ファイル」が出来上がります。
引数の -o オプションに続いて実行可能ファイル名、その後にコンパイルする対象のソースコードのファイル名を指定します。
$ rustc -o helloworld hello.rs
$ ls
hello.rs helloworld
実行可能ファイル”helloworld”ができました。
プログラムを実行してみます。
$ ./helloworld
Hello, world!!
プログラムが実行できました。
プログラムの解説
コード中の、次の部分に注目してください。
println!("Hello, world!!");
“println!”の部分は、「文字列を画面に出力せよ」という命令です。(文字列は文字が連なって列になったものです。)
“!”が最後についているのが奇妙な気がします。これは”println”が「マクロ」であるという意味です。Rustで用いる命令は「関数」や「マクロ」などの種類がありますが、これは後ほど説明します。
丸括弧”()”の中には命令に渡すデータを指定します。これを「引数(ひきすう)」と呼びます。文字列は “”(ダブルクオーテーション)で囲って指定します。
このような命令の最後には ; を付与します。”;”は、さまざまなところで出てきますが、ひとつの命令の終わりを締めくくるものだと思ってください。
では、上記を除いた部分、つまり次の部分に注目します。
fn main() {
...
}
これはRustで使う命令のひとつである「関数」を定義しています。”fn”に続いて関数名を指定します。
Rustのプログラムでは、最初に実行する関数として”main”という名前の関数を作ります。”()”の中には引数を指定しますが、今は使用しませんので空欄とします。
続いて、中括弧の中”{ … }”には、プログラムのコードを書きます。今回は、文字列を出力するだけなので println!(…)が書かれています。このような中括弧”{ … }”を、「ブロック」といいます。
ところで、もう一度ソースコードの全文をみてみましょう。
fn main() {
println!("Hello, world!!");
}
“println!()”の部分が1段右にズレているのがわかります。
これは「インデント(字下げ)」といいます。プログラムを見やすくするためのものです。インデントは、空白4つを使います。(他の言語では、tabキーでも代用できるものもあります)
インデントを用いなくてもプログラムを作成できますが、そのようなプログラムは非常に見にくい(醜い)ものになりがちなので、インデントを忘れないようにしましょう。
cargo によるプロジェクト管理
Rustでは、プログラムをソースコード単位でなくプロジェクト単位で管理するための機能をあらかじめ備えています。これは、”cargo”コマンドで管理できます。
##プロジェクト作成
※プロジェクトは、”~/rust”ディレクトリに作成することを忘れないでください。次回以降はとくに記載しません。
$ cd ~/rust
$
プロジェクトを新規作成するには、”cargo”に続いて”new”オプションを指定し、プロジェクト名を指定します。”hello2″プロジェクトを作成するには、次のようにします。
$ cargo new hello2
Created binary (application) `hello2` package
コマンドが完了すると “hello2” ディレクトリができていますので、”hello2″ディレクトリに移動します。
$ ls
hello hello2
$cd hello2
“hello2″ディレクトリ内には、”Cargo.toml”ファイルと、”src”ディレクトリができています。
“Cargo.toml”ファイルは、プロジェクトの設定ファルです。
“src”ディレクトリ内に、ソースコードができます。ソースコードのファイル名は”main.rs”です。
$ ls
Cargo.toml src
$ ls src
main.rs
コードの編集
ソースコードの”main.rs”を編集します。次のようにしましょう。
fn main() {
println!("Hello, world2!");
println!("Hello, world2!");
}
プロジェクトのビルド
cargoにより管理されたプロジェクトのプログラムの実行可能ファイルをつくるには、「ビルド」をおこないます。
ビルドは、”cargo”コマンドに”build”オプションを指定しておこないます。
これには、次のようにします。
$ cargo build
Compiling hello2 v0.1.0 (/mnt/e/wsl/rust/hello2)
Finished dev [unoptimized + debuginfo] target(s) in 2.37s
ビルドが完了すると、次のようにファイルやディレクトリができます。
$ ls
Cargo.lock Cargo.toml src target
実行可能ファイルは、”./target/debug”ディレクトリ内に出来ます。
$ ls target/debug/
build deps examples hello2 hello2.d incremental
プログラムを実行します。
$ ./target/debug/hello2
Hello, world2!
Hello, world2!
02. コメント
ソースコードのなかに、「コメント(comment)」を記述することができます。
コメントは、ただの説明文です。プログラムの動きには影響を与えません。
多くの場合、コメントは、そのコード(命令)が何をするかを説明するために書きます。
// スタイル
コメントは、 // に続いて記述します。
// と記述すると、その行のそれ以降はコメントとなります。
コメントは、行の途中でも記述できます。
fn main() {
// Hello, World2" と表示する
println!("Hello, world2!");
println!("Hello, world2!"); // Hello, World2" と表示する
}
また、コメントはコードのある行またはある部分を無効化するためにも使うことがあります。
たとえば、次のようにすると最初の”println!()”はコンパイルされなくなります(実行されません)。
fn main() {
// Hello, World2" と表示する
// println!("Hello, world2!");
println!("Hello, world2!"); // Hello, World2" と表示する
}
/* */スタイル
コメントとしたい部分を /* / で囲むと、その範囲がコメントとなります。/ で始まり / で終わることになります。
なお、// によるコメントも、/ */の範囲に含めることが出来ます。
fn main() {
/*
// Hello, World2" と表示する
// println!("Hello, world2!");
println!("Hello, world2!"); // Hello, World2" と表示する
*/
}
03. 変数 その1
変数(variable)を使うと、値やデータに名前をつけてそれを保持したり、計算に使ったりすることができます。
Rustの変数の機能は多彩なので、簡単なものから説明します。
Rustの変数は、おおきくわけて2種類あります。
-
- 不変変数(immutable variable): 定義したら値を変更できない変数
- 可変変数(mutable variable): 定義した値を変更できる変数
Rustの変数は、そのほかにも、スタックに積まれるもの、ヒープに積まれるもの、所有権があるもの、所有権のないもの、といった違いがあります。所有権については、だいぶ後で説明します。
03-01. 不変(immutable, イミュータブル)変数を使う(variable01)
まず、不変変数をつくり、それを利用するプログラムを作ってみます。
新たなプロジェクトvariable01を作ります。
$ cargo new variable01
Created binary (application) `variable01` package
ソースコードを編集します。
fn main() {
let count = 10;
println!("カウント数: {}", count);
}
プロジェクトをビルドして、実行します。
$ cargo build
Finished dev [unoptimized + debuginfo] target(s) in 0.03s
$ ./target/debug/variable01
カウント数: 10
このコードの解説をしていきます。
下記のコードは、”count”という名前の変数を作成し、その変数に10の整数値を代入します。
つまり、変数を作成するには、”let”のあとに続いて変数名を指定し、その値を = を挟んで指定します。
このとき、10 のようにプログラム中に直接数値などのデータを埋め込むことがあります。このようなデータを「リテラル」といいます。数値の場合は「数値リテラル」といいます。
代入すると、その値が変数に束縛されます。この場合は、10という値(のデータ)をcountという変数に束縛します。
let count = 10;
以下のコードは、文字列の中に “{}” を含んでいます。”{}”はプレースホルダです。この部分に、その後につづく引数指定するデータを埋め込んで表示するという意味です。
println!("カウント数: {}", count);
もし”{}”が複数ある場合は、左側から順番にあてはまります。なお、引数は変数でもリテラルでもどちらでもいいです。
コードを次のように変更して実行してみましょう。
fn main() {
let count = 10;
println!("カウント数: {}, {}, {}", count, 20, 100);
}
引数に指定した3つの値が表示されました。
$ cargo build
Compiling variable01 v0.1.0 (/mnt/e/wsl/rust/variable01)
Finished dev [unoptimized + debuginfo] target(s) in 0.92s
$ ./target/debug/variable01
カウント数: 10, 20, 100
fn main() {
let count :i32 = 10;
println!("カウント数: {}", count);
}
ところで、ここで定義した変数は”不変変数”で、最初に代入した値を変更することはできません。
実験として、一旦代入した値を変更してみるとどうなるか、確認してみましょう。
コードを次のように変更して、ビルドしてみましょう。変更点は”count = 100;”が追加されたことです。
これは、変数”count”に100の値を代入することを意味します。つまり、最初10だった値を100に変更するということです。
fn main() {
let count = 10;
count = 100;
println!("カウント数: {}, {}, {}", count, 20, 100);
}
このコードのビルドは次のようなエラーメッセージを伴うエラーとなります。ビルドできません。
$ cargo build
:(省略)
cannot assign twice to immutable variable
:(省略)
エラーメッセージは「不変変数に2回、値を代入することはできません」という意味です。
03-02. 可変(mutable, イミュータブル)変数を使う(variable02)
変数を可変(ミュータブル)とすると、変数の値を変更できます。
この練習をするためのプロジェクト”variable03″を作成しましょう。
$ cd ~/rust
$ cargo new variable02
Created binary (application) `variable02` package
$ cd variable02
先ほど作成したコードをもとに、次のようにコードを変更しましょう。
fn main() {
let mut count = 10;
count = 100;
println!("カウント数: {}, {}, {}", count, 20, 100);
}
変更点がわかりましたか?
“let”の後に”mut”を付け加えただけです。(“mut”は、”mutable”の意味です。)
コードが入力できたら、さっそくbuildしてみましょう。
warning(警告)が表示されますが、この時点では気にしなくても大丈夫です。
$ cargo build
Compiling variable02 v0.1.0 (/mnt/e/wsl/rust/variable02)
warning: value assigned to `count` is never read
--> src/main.rs:2:13
|
2 | let mut count = 10;
| ^^^^^
|
= note: `#[warn(unused_assignments)]` on by default
= help: maybe it is overwritten before being read?
warning: `variable02` (bin "variable02") generated 1 warning
ビルドができたら、プログラムを実行してみます。
countの値が100となったのが確認できます。
$ ./target/debug/variable02
カウント数: 100, 20, 100
03-03. 変数の型(variable03)
変数が格納できるデータは、その特性を「型(かた, type)」で特定します。
Rustでは、型はコンパイル時に確定しておく必要があります。
このような制限のある言語を「静的型付き言語」といいます。RustやC言語、Java、Swift、C#等がそれに該当します。
型を確定させる方法は、直接指定するか、明確にそれとわかるように代入などのコードを記述することです。
03-03a.型の指定
練習のためのプロジェクトを作成しましょう。
$ cd ~/rust
$ cargo new variable03
Created binary (application) `variable02` package
$ cd variable02
練習のためのコードは、次のとおりです。
コードを良く見比べてください。
“:i32″が変数名の前に追加されています。”i32″は、型のひとつで、”符号付き整数(32bit)”という意味です。
型指定は、それぞれの変数の右側に付与します。
fn main() {
let mut count :i32 = 10;
count = 100;
println!("カウント数: {}, {}, {}", count, 20, 100);
}
さっそく、プログラムを実行してみましょう
$ cargo build
Compiling variable03 v0.1.0 (/mnt/e/wsl/rust/variable03)
:(省略)
Finished dev [unoptimized + debuginfo] target(s) in 2.39s
$ ./target/debug/variable03
カウント数: 100, 20, 100
プログラムの動作自体は変わりません。
コードの相違点は、変数の型を明示的に指定したかどうか、ということです。
Rustコンパイラは、コンパイル時に代入する値の型が明確に判明していれば、その型を使うものとしてコンパイルします。この場合は型を指定しなくてもよいです。
しかしながら、その変数の使用目的を明確化するために、いつも変数の型を指定することを心がけたほうが良いでしょう。
03-03b.利用できる型
Rustで利用できる型には、以下のようなものがあります。
-
- スカラー型: ひとつのデータを表現します
- 複合型: いくつかのデータを組み合わせて表現します
スカラー型
スカラー型は、ひとつのデータを表現します。
ひとつのスカラー型変数にはひとつの値が入ります。
●整数型
“isize”, “usize”は、ビット長はプロセッサにより異なります。32bitのアーキテクチャであれば32bitで、64bitのアーキテクチャであれば54bitとなります。
●浮動小数点型
浮動小数点の型名は、”f64” または “f32” です。型名が表す通り、f64のほうが精度がよいです。最近のパソコンやサーバ向けプロセッサではf64でもf32でも同等の計算速度です。組込みなどで使う「小さい」CPUでは、計算速度が異なるかもしれません。
●論理値型
論理値型は2つの値のみをとる変数です。trueまたはfalseの値をとります。
型名は”bool”です。
●文字型
文字型は、1文字だけの文字を表現します。型名は”char”です。
2文字以上が連なった場合は”文字列”と呼ばれ、文字とは区別されます。
文字コードはUnicodeが使用されます。
(※C言語の場合とは違い、1文字分のデータをしっかり取り扱うことができます。)
複合型
●タプル型
タプル型を使うと、複数の値を組み合わせてひとつのデータとして取り扱うことができます。それぞれの値は異なる型とすることができます。
●配列型
配列を使うと、同じ型の値を複数取り扱うことができます。
03-03c.リテラルの表現
リテラル(literal, 直値/直定数)は、プログラム中に直接埋め込む数値や文字などのデータです。
例えば、以前紹介した下記のコードの数値部分は、「リテラル」です。
fn main() {
let mut count = 10;
count = 100;
println!("カウント数: {}, {}, {}", count, 20, 100);
}
リテラルは、用いるデータの種類によって適切な書き方があります。その例を紹介します。
10進数はそのまま書きます。8進数は頭に0oを、16進数は0xを、2進数は0bをつけます。
桁の区切りを、わかりやすいように _ を使って区切ります。区切らなくてもいいです。区切りの桁数がでたらめでも大丈夫なようですが、わかりやすい区切りにしたほうがいいでしょう。
次回へつづく