Rustをはじめよう その2(プログラム作成&実行編)

三菱総研DCS 技術戦略部の加藤です。 この記事では、プログラミング言語 Rust(ラスト)の簡単なプログラム作成と実行方法についてまとめます。

Hello world プログラムの作成と実行

前回の記事で、Rust のプログラムを作成・実行する環境を整えました。早速、Rust のプログラムを書いて実行してみましょう!
以下では、プログラムのコンパイルから実行までの手順を、手順がシンプルなものから順番に説明します。

  1. rustcコマンドを使用したコンパイルと実行
     rustcコマンドはRustのプログラムをコンパイルし、実行可能ファイルを生成します。生成した実行可能ファイルを実行して、プログラムが正しく動作することを確認します。
  2. Cargoを使用したビルドと実行
     今回のサンプルプログラムではあまり恩恵は受けられませんが、ビルドシステムとパッケージマネージャを兼ねる「Cargo」を使用することで、プログラムが依存するパッケージの管理などが容易となります。
  3. VSCodeを使用したプログラムの実行
     VSCode上からCargoを使用したビルドを実施し、プログラムを実行します。
     この方法では、いちいちコマンドプロンプトを開いてコマンドを打つ必要がないため、実際の開発で最も頻繁に使用すると思われます。


1. rustc コマンドを使用したコンパイルと実行

1.1. Rustファイルの作成とコンパイル

任意のフォルダに「hello_world.rs」という名称のファイルを作成し、以下のコードを入力します(プログラムの詳細については後続のセクションで解説します)。なお、Rust のプログラムを記述したファイル(.rs ファイル)の名前に 2 単語以上が含まれる場合は、今回のように単語同士をアンダースコア(_)でつなぐスネークケースで命名するのが通例です。

hello_world.rs

fn main() {
  // Hello, world!とコンソール出力する。
  println!("Hello, world!");
}

では、このプログラムを実行してみましょう。まずは、コマンドプロンプトから実行します(VSCode 上から実行する方法は後述します)。
コマンドプロンプトで「hello_world.rs」を作成したフォルダまで移動し、以下のコマンドを実行します。

rustc hello_world.rs

上記のコマンドを実行すると、プログラムがコンパイルされ、「hello_world.rs」を作成したフォルダ内に実行可能ファイル(コンパイル済みのプログラムで拡張子が.exe のファイル)が生成されます。以下のコマンドで、作成されたことが確認できます。

dir /b

(出力例)

hello_world.exe
hello_world.pdb
hello_world.rs

なお、rustc コマンドでプログラムのコンパイルを実行すると、.exe ファイルのほか、.pdb ファイルも生成されます。これは、プログラムのデバッグに使用するファイルで、今回は使用しません。

1.2.プログラムの実行

実行可能ファイルを実行してみましょう。以下のコマンドを実行すると、コマンドプロンプト上に「Hello, world!」という文字列が表示されます。

hello_world.exe

上記の手順で、Rust で書かれたプログラムを実行することができました。

ここで、上記手順について少し補足をします。
JavaScript や Python などの「インタプリタ型言語」のプログラムを書いた経験のある方は、Rust ではプログラムを実行する前にコンパイル(上記であれば rustc コマンド)する必要のあることが気になっているかもしれません。
実際、JavaScript や Python はコンパイルが不要であり、実行時にプログラムを逐一解析して、コンピュータに理解できる形式に変換する「インタプリタ型言語」です。そのため、コードを書いたら即座に実行できるというメリットがある一方で、後述の「コンパイラ型言語」と比較すると、プログラムを逐次解析することの影響で実行速度が遅いという特徴もあります。
Rust は「コンパイラ型言語」であり、プログラムを実行する前にプログラム全体をコンピュータに理解できる形式に変換(コンパイル)するプログラミング言語です。コンパイラ型言語は、文字通り実行前にコンパイルが必要なため、プログラムの実行までに時間がかかるという特徴があります。ただし、コンパイルさえ終われば、プログラムの実行自体はインタプリタ型言語よりも高速というメリットもあります。

ここまでの手順で、Rust のプログラムを記述してコンパイルを行い、プログラムを実行することができました。次に、Cargo というツールを使用したプログラムのビルドについて解説します。

2. Cargo を使用したビルドと実行

Cargo は、Rust のビルドシステムとパッケージマネージャの役割を担うツールです。Java で開発をしたことがある方であれば、Maven や Gradle に近い機能を持ったツールである、という説明が理解しやすいと思います。

これ以降の手順では、Cargo を使用してプログラムをビルド(コンパイルを含む)し、実行することにします。なぜなら、実際の案件で開発するような多数のパッケージに依存した複雑なプログラムを作成する場合は、基本的に Cargo が使用されるためです。前述の「hello_world.rs」のように、パッケージに依存しないシンプルなプログラムであれば rustc コマンドでコンパイルしてもよいのですが、より汎用性の高い方法である Cargo の使用方法をここで覚えておくと、今後も役に立つと考えます。

2.1. Cargoのインストール確認

Cargo は、rustup をインストールした際に rustc などと合わせてインストールされています。
コマンドプロンプトで以下のコマンドを実行すると、Cargo のバージョン情報が表示され、Cargo がインストールされていることを確認できます。

cargo --version


2.2. パッケージの作成とRustプログラムの生成

実際に Cargo を使用してみましょう。Cargo は、Rust のプログラム(.rs ファイル)や依存関係を「パッケージ」という単位で管理します。そのため、まずはこのパッケージを作成する必要があります。
以下のコマンドを実行して、「hello_cargo」という名称のパッケージを新規作成します。

cargo new hello_cargo

コマンドを実行したフォルダ内に「hello_cargo」という名前のフォルダが生成され、フォルダ内には以下のフォルダとファイルが生成されます。

  • .git フォルダ: バージョン管理システムの Git が使用するフォルダです。今回は使用しません。
  • src フォルダ: source の略称で、このフォルダの配下に Rust のプログラム(.rs ファイル)を作成します。
  • .gitignore ファイル: .git フォルダと同様に Git で使用するファイルで、今回は使用しません。
  • Cargo.toml ファイル: Cargo の設定ファイルです。

src フォルダには「main.rs」というファイルが生成されています。これは、Cargo がパッケージを新規作成する際に自動的に生成される、Rust のプログラムです。
「main.rs」の内容は、以下のようになっています。

main.rs

fn main() {
    println!("Hello, world!");
}

このコードに見覚えがありませんか? これは、さきほど手動で作成した「hello_world.rs」と(ほぼ)同じコードです!

2.3. プログラムのビルド

作成したプログラムをビルドします。ビルドとは、プログラムの実行に必要なパッケージの収集や依存関係の解決を行った上でコンパイルを実施し、実行可能ファイル(.exe ファイル)を生成する処理のことです。
以下のコマンドでビルドを実行します。なお、以下のコマンドはコマンドプロンプト上で hello_cargo フォルダに移動してから実行してください。

cargo build

ビルドを実行すると、hello_cargo フォルダに新たに target フォルダが生成されます。また、target フォルダ配下の debug フォルダ内に実行可能ファイル「hello_cargo.exe」が生成されます。hello_cargo.exe をコマンドプロンプトから実行すると、画面上に「Hello, world!」と表示されます。

なお、上述のビルド&実行可能ファイルの実行は、以下のコマンドを使用すると一括で実行することができます。

cargo run

上記の cargo run コマンドは、ビルドが未済の場合はビルドを実行し、生成した実行可能ファイルを実行するコマンドです。

2.4. コンパイル可否のチェック

作成したプログラムのコンパイル可否をチェックする方法をご紹介します。
以下のコマンドで、プログラムがコンパイル可能かチェックできます。

cargo check

なお、cargo check コマンドの「check」の部分を縮めて「cargo c」でも同様に実行できます。
以下で、cargo check コマンドの挙動を簡単に確認します。
まず、hello_cargo\src\main.rs ファイルの内容を以下のように変更し、意図的にコンパイルエラーが起こる状態にします。

main.rs(変更後)

fn main() {
    println!("Hello, world!");

元の main.rs の末尾から「}」を削除しました。閉じかっこがなくなったため、このプログラムはコンパイルエラーになることが予想できます。
この状態で cargo check コマンドを実行すると、予想通りコンパイルエラーとなる旨が表示されます。

確かに「error: this file contains an un-closed delimiter」(エラー: このファイルは閉じられていない区切り文字を含んでいます)と表示され、閉じかっこがないことが指摘されます。

main.rsの末尾に「}」を追加して、正しくコンパイルできる状態に戻しておきましょう。以下に元の(コンパイルできる)プログラムを示します。

main.rs

fn main() {
    println!("Hello, world!");
}

念のため、cargo check コマンドや cargo run コマンドを実行して、コンパイルエラーが解消され、プログラムが実行できることを確認してください。

3. VSCode を使用したプログラムの実行

ここまでの手順では、せっかく準備した VSCode を使用していませんでした。以降は VSCode 上で Rust をコーディング、実行していきます。

3.1. 統合開発環境の準備

Cargo が生成したパッケージのフォルダ(今回は hello_cargo)を VSCode で開きましょう。この際に、VSCode の画面右下にダイアログが表示され、RLS が未インストールである旨が表示されます。「OK」ボタンをクリックすると RLS がインストールされ、これで統合開発環境(IDE)としての準備が完了します。

3.2. Rustプログラムのデバック準備と実行

Rustのプログラムをデバッグするための準備をします。画面左側の「実行とデバック」(虫のアイコン)をクリックし、「launch.jsonファイルを作成します。」と書かれた箇所をクリックします。画面上部に「環境を選択」と書かれたメニューが表示されるので「LLDB」を選択し、表示されるダイアログボックスでは「Yes」ボタンをクリックします。

デバッグ画面上部の緑色の実行ボタン(▶)をクリックするか、キーボードのF5キーを押します。こうすることで、VSCode上でcargo buildを実行し、問題なければ生成した実行可能ファイルを実行することができます(つまりcargo runと等価)。

画面下部の「ターミナル」タブにコンソール出力の結果(Hello, world!)が表示されます。

以上で、VSCode上でRustのプログラムを実行できるようになりました!

Hello world プログラムの詳細

ここまで実行してきた Hello world プログラムですが、プログラムの内容については説明していませんでした。最後に、プログラムをいくつかのパーツに分割した上で、それぞれの意味を解説して本記事を終わりたいと思います。

fn main () { }

上記のコードは、Rust における関数を定義しています。関数は JavaScript などの関数(function)の役割と似ており、引数を受け取って値を返却する処理の固まりを表します。
main 関数は、Rust のプログラムを実行した場合に最初に実行される関数(処理)です。
関数名の後ろの()の中には引数を宣言できますが、今回作成した main 関数は引数が不要なため空になっています。引数が必要な場合は、引数名とデータ型を合わせて書きます(たとえば(x: i32, y: i32)は x と y という仮引数名で符号付き 32bit 整数型の引数を取ることを表す)。
また、()の後ろには ->(マイナスと大なりの記号) とデータ型を書くことで戻り値を宣言できます(たとえば -> i32 は符号付き 32bit 整数型の戻り値を表す)。ただし、今回作成した main 関数は戻り値が不要なため、こちらも引数と同様に空となっています。Java などの言語では、戻り値を返さない場合も何らかのワード(void など)を記述する必要がありますが、Rust では何も書かないことで戻り値なしを表現します。一方、JavaScript や Python などのスクリプト言語では、戻り値を返却する場合でも関数宣言の段階で型を指定する必要はありませんが、Rust では型を指定する必要があります(指定せずに値を返却するとコンパイルエラー)。
なお、引数や戻り値といった関数の実装方法については、次回の投稿でより詳しく説明します。
関数で実行する処理は、{}(中かっこ)で囲みます。この片方がないとコンパイルエラーになってしまうことは、cargo check で確認したとおりです。

println!("Hello, world!");

これは、main 関数の中かっこの間に記述した処理です。全体として「Hello, world!」という文字列を画面に表示する、という処理になっています。
println!で、println という名前のマクロを呼び出しています。マクロを呼び出す場合は、マクロ名の後ろに!(Exclamation Mark)を付けます。一見すると関数を呼び出しているようにも見えますが、仮に println 関数を呼び出す場合は println(!なし)で呼び出します。マクロの特徴については詳説しませんが、可変長の引数を取れるなど、関数とは異なる機能があります。
println!後方の()内の"Hello, world!"は、この文字列を引数として println マクロに渡すという意味です。println マクロは、引数として受け取った文字列を画面に出力します。
この文の末尾には;(セミコロン)があります。Rust のプログラムの文末は、基本的に;で終わります。

まとめ

この記事では、Rust の簡単なプログラム作成・実行について解説しました。
次回は、他のプログラミング言語とも共通する、Rust の基本的な文法事項(データ型、制御構文など)を紹介します。

参考文献

  • 「The Rust Programming Language: 2nd Edition」, https://doc.rust-jp.rs/book-ja-pdf/book.pdf 2020 年 2 月 26 日アクセス
  • κeen,河野 達也,小松礼人(2019)『実践 Rust 入門[言語仕様から開発手法まで]』 技術評論社
その1 |  その2 | その3