ザ・ブックでRustの学習開始

これまで、Windows、macOS、LinuxなどのプラットフォームでRustプログラミングを始めるための準備を行ってきた。今回までの説明で、多くのプラットフォームでのセットアップをカバーできたのではないかと思う。それではコーディングを始めていこう。

Rustを学ぶ方法はいくつかあるが、Rustチームが公開している公式のドキュメントに沿って学習を進めるのがよいだろう。このドキュメントは通称「ザ・ブック(the book)」と呼ばれており、Webですべて公開されている。「ザ・ブック」を最初から進めていけば、終わった頃にはRustの全体像が把握できるようになっている。簡単なドキュメントではないが、取り組みがいのあるドキュメントだ。ザ・ブックは次のページで閲覧できる。

The Rust Programming Language - The Rust Programming Language

The Rust Programming Language

ザ・ブックは、プログラミング経験やターミナル使用経験があることを前提として組まれている。これまでにプログラミングの経験がなかったり、ターミナルをまったく使ったことがなかったりすると、この本の内容は相当難解に感じられるかもしれない。C/C++、Java、C#辺りの経験があると、この本の内容はそれほど難しくはなく、既存の知識をベースにRustの知識を取り込んでいくことができるだろう。

ただし、プログラミングの経験がなくても、学習は不可能ではないと思う。現在はインターネットでさまざまなドキュメントにアクセスできるので、それらを使いながらザ・ブックを読み進めていくことは可能だ。本連載では、ザ・ブックに沿う形でRustの紹介を進めていく。

○Hello World! に挑戦{#ID2}

プログラミングの最初のステップでは、「Hello World」と呼ばれるプログラムを作成することが多い。これは文字列を標準出力に表示するプログラムとして組まれることが多い。以下、「Hello World」を作っていく。

まずは、次のようにターミナルでcargoコマンドを実行する。これで「hello_world」というディレクトリと、同じ名前のRustのプロジェクトが作成される。

cargoコマンドでhello_worldというプロジェクトを作成

cargo new hello_world

作成されたディレクトリに移動する。または、Visual Studio Codeを使っている場合には、この段階で作成したフォルダを開く。そのあとで統合ターミナルを起動すると自動的にこのディレクトリへ移動してくれる。

作成したディレクトリ(プロジェクト)へ移動

cd hello_world

cargo build hello_worldで次のようなディレクトリ/ファイルが作成される。

cargo new hello_worldで作成されるディレクトリとファイル

% tree -a

.

├── .git

│ ├── HEAD

│ ├── config

│ ├── description

│ ├── hooks

│ │ └── README.sample

│ ├── info

│ │ └── exclude

│ ├── objects

│ │ ├── info

│ │ └── pack

│ └── refs

│ ├── heads

│ └── tags

├── .gitignore

├── Cargo.toml

└── src

└── main.rs

10 directories, 8 files

%

Visual Studio CodeでRustプロジェクトの作成と編集 - macOS

このとき作成されるmain.rsというファイルがRustのメインプログラムファイルだ。デフォルトでは次のような内容が書いてある。最初から書いてあるので、今回は特に何かを書き換えるとか新しく追加する必要はない。

main.rs

fn main() {

println!("Hello, world!");

}

ソースコードは次のようにcargo buildでビルドする。

cargo buildでプロジェクトをビルド

% cargo build

Compiling hello_world v0.1.0 (/Users/daichi/Documents/rust_testbed/hello_world)

Finished dev [unoptimized + debuginfo] target(s) in 3.65s

%

cargo buildでビルドすると次のようにコンパイル後のバイナリファイルとかいろいろ生成される。

ビルド後に生成されたファイルなど

% tree -a

.

├── .git

│ ├── HEAD

│ ├── config

│ ├── description

│ ├── hooks

│ │ └── README.sample

│ ├── info

│ │ └── exclude

│ ├── objects

│ │ ├── info

│ │ └── pack

│ └── refs

│ ├── heads

│ └── tags

├── .gitignore

├── Cargo.lock

├── Cargo.toml

├── src

│ └── main.rs

└── target

├── .rustc_info.json

└── debug

├── .cargo-lock

├── .fingerprint

│ └── hello_world-b6022b4b67fe021d

│ ├── bin-hello_world-b6022b4b67fe021d

│ ├── bin-hello_world-b6022b4b67fe021d.json

│ ├── dep-bin-hello_world-b6022b4b67fe021d

│ └── invoked.timestamp

├── build

├── deps

│ ├── hello_world-b6022b4b67fe021d

│ ├── hello_world-b6022b4b67fe021d.d

│ └── hello_world-b6022b4b67fe021d.dSYM

│ └── Contents

│ ├── Info.plist

│ └── Resources

│ └── DWARF

│ └── hello_world-b6022b4b67fe021d

├── examples

├── hello_world

├── hello_world.d

├── hello_world.dSYM -> deps/hello_world-b6022b4b67fe021d.dSYM

└── incremental

└── hello_world-rusv2vn618tz

├── s-fo0vunx5ug-1jtw5y5-1agx9pzuh2ieo

│ ├── 1ye8vd56m3l4y5ok.o

│ ├── 3k4jv3xs068t3jij.o

│ ├── 49198m7t4jd6cx27.o

│ ├── 4cjto95ztzjkx88l.o

│ ├── 5cx24cz1ws0kkx2f.o

│ ├── 6602w3zlgtzklph.o

│ ├── dep-graph.bin

│ ├── query-cache.bin

│ └── work-products.bin

└── s-fo0vunx5ug-1jtw5y5.lock

25 directories, 31 files

%

生成したバイナリはcargo runのようにcargoコマンドを使うことで実行できる。標準出力に「Hello, world!」という文字列が出力されていることがわかるだろう(Hello World!以外の文字列はcargoが出力したもので、作成したプログラムが出力したものではない)。

cargo runでアプリケーションを実行

% cargo run

Finished dev [unoptimized + debuginfo] target(s) in 0.01s

Running `target/debug/hello_world`

Hello, world!

%

ビルドで生成されたアプリケーション本体は「./target/debug/hello_world」であり、これを直接実行しても同じ結果が得られる。

ビルドで生成されたアプリケーションを直接実行

% ./target/debug/hello_world

Hello, world!

%

上記サンプルはmacOSで実行したものだ。Windowsでも同じようなことが起こる。たとえばWindows 10でプロジェクトを作成してビルドすると次のようなフォルダやファイルが生成される。

Windows 10でビルドしたあとのフォルダ/ファイル

PS C:\Users\daich\Documents\rust_testbed\hello_world> wsl tree -a

.

├── .git

│ ├── HEAD

│ ├── config

│ ├── description

│ ├── hooks

│ │ └── README.sample

│ ├── info

│ │ └── exclude

│ ├── objects

│ │ ├── info

│ │ └── pack

│ └── refs

│ ├── heads

│ └── tags

├── .gitignore

├── Cargo.lock

├── Cargo.toml

├── src

│ └── main.rs

└── target

├── .rustc_info.json

├── debug

│ ├── .cargo-lock

│ ├── .fingerprint

│ │ └── hello_world-6706d124191574b7

│ │ ├── bin-hello_world

│ │ ├── bin-hello_world.json

│ │ ├── dep-bin-hello_world

│ │ └── invoked.timestamp

│ ├── build

│ ├── deps

│ │ ├── hello_world.d

│ │ ├── hello_world.exe

│ │ └── hello_world.pdb

│ ├── examples

│ ├── hello_world.d

│ ├── hello_world.exe

│ ├── hello_world.pdb

│ └── incremental

│ └── hello_world-3lr0nf7wfwzik

│ ├── s-fo0w10103d-13vkha3-3ehaunh1f1n5p

│ │ ├── 1292prq4vl6p7bft.o

│ │ ├── 2vb4pxq4jeqs5fv.o

│ │ ├── 3lsrtqnyc44zwxmh.o

│ │ ├── 3xhd6ovn5xdyrp4w.o

│ │ ├── 52f7264h2yvjqq0g.o

│ │ ├── c74uhskdddqcvzx.o

│ │ ├── dep-graph.bin

│ │ ├── query-cache.bin

│ │ └── work-products.bin

│ └── s-fo0w10103d-13vkha3.lock

└── rls

├── .rustc_info.json

└── debug

├── .cargo-lock

├── .fingerprint

│ ├── hello_world-2021aa1441466306

│ │ ├── bin-hello_world-2021aa1441466306

│ │ ├── bin-hello_world-2021aa1441466306.json

│ │ ├── dep-bin-hello_world-2021aa1441466306

│ │ └── invoked.timestamp

│ └── hello_world-d80639819864fd47

│ ├── dep-test-bin-hello_world-d80639819864fd47

│ ├── invoked.timestamp

│ ├── test-bin-hello_world-d80639819864fd47

│ └── test-bin-hello_world-d80639819864fd47.json

├── build

├── deps

│ ├── hello_world-2021aa1441466306.d

│ ├── hello_world-d80639819864fd47.d

│ ├── libhello_world-2021aa1441466306.rmeta

│ ├── libhello_world-d80639819864fd47.rmeta

│ └── save-analysis

│ ├── hello_world-2021aa1441466306.json

│ └── hello_world-d80639819864fd47.json

├── examples

└── incremental

├── hello_world-2x3hygxnyp3zg

│ ├── s-fo0w0s2rom-9h9try-1xb6oot3mp4bn

│ │ ├── dep-graph.bin

│ │ ├── query-cache.bin

│ │ └── work-products.bin

│ └── s-fo0w0s2rom-9h9try.lock

└── hello_world-3927p7pxpczei

├── s-fo0w0su47m-dsp6dd-24i8uuxu38giu

│ ├── dep-graph.bin

│ ├── query-cache.bin

│ └── work-products.bin

└── s-fo0w0su47m-dsp6dd.lock

34 directories, 55 files

PS C:\Users\daich\Documents\rust_testbed\hello_world>

Windows 10だと生成されるアプリケーションは「.\target\debug\hello_world.exe」のように拡張子がついたものになる。

Windows 10で生成されたアプリケーション

PS C:\Users\daich\Documents\rust_testbed\hello_world> .\target\debug\hello_world.exe

Hello, world!

PS C:\Users\daich\Documents\rust_testbed\hello_world>

Visual Studio Code - Windows 10の使用例

基本的にはこんな感じだ。main.rsをコンパイルするだけならrustcというコマンドで実行できる。ただ、ファイルが増えてくると結局cargoを使うことになるし、GitHubに登録されているRust関係のプロジェクトはcargoを使うものになるので、最初からcargoを使ったビルドに慣れておいたほうがよいだろう。

main.rsを読む

通常、Hello Worldで使われるソースコードはすごく短い。標準出力に文字列を出すためのもので、とりあえずビルド/コンパイルする方法を学ぶといった目的で使われるものだからだ。Rustの場合も例にもれず短い。しかし、このファイルはいくつかのことを教えてくれる。

main.rs

fn main() {

println!("Hello, world!");

}

ここでの要点を簡単にまとめると次のようになる。

Rustファイルの拡張子は「.rs」

main.rsというファイルを作成し、ここにRustのソースコードを記述する。main.rsファイルにはmain関数を記述する。main.rsのmain関数がプログラム実行の起点となる

関数はfnで作成

関数名後の()はパラメータ指定

関数本体は{}で囲う

インデントは空白4文字

printlnは関数

println!はRustマクロ。Rustでは!を使ってマクロを呼び出す

行は;で終了する

main.rsからはわからないのだが、次の規則もここで取り上げておく。

Rustファイルの名前に複数の単語を含める場合にはアンダースコア「_」で単語間を接続する

Rustの学習を進めていけば上記内容は慣れてくると思うので、今あまり強く意識する必要はないと思う。そういうものか、くらいに思っておけばよいんじゃないだろうか。

まずは試してみよう

今回はここまでだ。環境を整えたらHello Worldを作成してビルドを実行を確認する。まずは、これを実行しよう。プログラミングの学習はなによりも実演が大切だ。

今回はVisual Studio Codeを使う場合の方法も紹介したが(フォルダを開くタイミングなど)、次回からは説明しないので、適宜対応してもらえればと思う。

参考資料

The Rust Programming Language - The Rust Programming Language