昨今のRustの勢いを見ているとRustを避けて通る事はできなそうだ。そこでRustで遊ぶ事にした。
開発環境を構築する
Rustコンパイラやその他の開発ツールをインストールするには rustup
1を使う。公式ドキュメントに従い以下のコマンドでインストールする。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
これによりコンパイラである rustc
や、パッケージツールである cargo
などが使用可能になる。もしインストールディレクトリに PATH
が通っていない場合、 PATH
を通す必要がある。このあたりの手順は rustup
の出力に記載されているため、それに従い設定する。おそらく使用しているシェルごとの設定方法が説明されているだろう。
テキストエディタの設定
プログラミング作業は基本的にはテキストエディタで行う2。IDE3を使う人もいるかもしれないが、それは、IDEの中に実装されているテキストエディタを使っているから、結局のところテキストエディタを使っている。僕はEmacsというテキストエディタが好きだから、ここでEmacsの設定を少しだけ行う事にする。
今回はコードハイライト4されるようにするため、EmacsのRust用メジャーモードである rust-mode
をインストールする。
M-x package-install RET rust-mode RET
他にも便利な機能はたくさん実装されているけれど、そういうものは必要だと心底思えた時に入れればいい。
最初のプログラムはHello, world!から
どんな時でもプログラミングを始める時は「Hello world!」から始めるようにしている。それは、しばらく使っていなかった言語であっても同じだ。「Hello world!」の良い所は、プログラミングが簡単であり、コンパイルや実行方法などの基本的な事を確認できる所だ。まずは helloworld.rs
という名前でファイルを作り、コードを実装する。
fn main () {
println!("Hello, world!");
}
main
という関数が1つあるコードができた。これをコンパイルする。
rustc helloworld.rs
すると helloworld
という実行可能なバイナリファイルが出力される。実際に実行してみよう。
./helloworld
Hello, world!
Hello, world!
という文字列が表示された。Rust、完全に理解した。
ファイル分割
更にRustの理解を深める。プログラムのソースコードは、長くなると管理が難しくなっていく。通常はある程度大きくなった時点でファイルを分割する。ファイルの分割や取り込みの方法は各言語ごとにやり方が異なる。Rustではどうすればファイル分割できるのかを確認する。
Rustでソースコードを分割するには、Rustをモジュール化する方法が簡単そうだ。分割したいコードをモジュールとして定義し、そのモジュールを別ファイルに分け、モジュール内で定義されたものを取り込んで使うという方法が良さそう。
tree example_module
example_module |-- main.rs `-- testing.rs
例として、このようなファイル構成とする。 main.rs
にはエントリーポイントとしての main
関数を定義し、 testing.rs
には分割対象の関数として print_hellowolrd
を定義する。この関数は、モジュールとして読み込んで使用できるように、関数に pub
を宣言し、 pub fn println_helloworld()
のように定義しておく。この関数を main.rs
で使うため mod
を使い、 mod testing;
のように取り込みを宣言する。そして testing::println_helloworld();
のような形で関数を利用する。
main.rs
testing.rs
このコードをビルドする。
rustc example_module/main.rs
生成された実行可能ファイルを実行する。
./main
Hello, world!
分割できた。
ビルドと実行を同時に行う
Rustで実装したコードを実行可能なバイナリにするためには、ビルドを行う必要がある。しかしコードを少し変更して、ビルドし、実行して動作確認をするという、スクリプト言語のような開発スタイルを好む場合、この手順は手間だ。この手順が統合されると、実装と動作確認のフィードバックループが早くなるため開発効率が上がる。
Rustのプロジェクトマネージャ cargo
には、 cargo run
というサブコマンドがあり、ビルトと実行を同時に行う事ができる。この方法を使うには、 Cargo.toml
というファイルを作成しプロジェクト化する必要がある。
ただ、現時点ではプロジェクト化する必要はなく、シンプルな1つだけのファイルを指定して、ビルドと実行を出来れば良い。そこで cargo-script
を使う事にする。まずはこれをインストールする。
cargo install cargo-script
インストールできたら cargo script
コマンドが使用できるようになる。
cargo script helloworld.rs
Hello, world!
今後はこれを使っていく事にする。
コマンド引数
コマンドラインツールは、コマンド引数やオプション引数を指定して、挙動を変更する事が多い。これらをプログラムに組み込む方法を確認する。
コマンド引数は、 std::env
の args()
を使って取得できる。ここではコマンド引数を標準出力に表示する。
use std::env;
fn main () {
let args: Vec<String> = env::args().collect();
for (i, arg) in args.iter().enumerate() {
println!("{}: {}", i, arg);
}
}
cargo script
を使って実行する。
cargo script example_args.rs a b c
0: Users/DUMMY.cargo/binary-cache/release/example_args 1: a 2: b 3: c
オプション引数とは、 -p
や --port
、 -p 3000
や --port 3000
といったような ハイフン記号で指定された引数とそれに続く値だ。これを自力で解析するのは手間なので、 clap
や structopt
といったクレートを使う。ただし今は必要はないので使わない。
標準入力から値を受け取る
ユーザからの入力を待ち、入力された値を標準出力に表示し終了する簡単なプログラムを書いてみる事にする。
use std::io;
use std::io::Write;
fn main () {
let mut input = String::new();
print!("INPUT: ");
io::stdout().flush().unwrap();
io::stdin()
.read_line(&mut input)
.expect("Failed to read");
let input = input.trim();
println!("Ok: {}", input);
}
プログラムを実行すると、入力待ちの状態になり、入力すると標準出力にその文字列を表示し終了する。
cargo script -- example_stdin_read_one.rs
このプログラムを少し発展させ、入力をループで何度も待つようにする。そして exit
と入力したら、 Bye!!
という文字列を表示し終了するように修正する。
use std::io;
use std::io::Write;
fn main () {
loop {
let mut input = String::new();w
print!("INPUT: ");
io::stdout().flush().unwrap();
io::stdin()
.read_line(&mut input)
.expect("Failed to read");
let trimed_input = input.trim();
if (trimed_input == "exit") {
println!("Bye!!");
break;
}
println!("Ok: {}", trimed_input);
}
}
実行は以下のようにする。
cargo script -- example_stdin_read_loop.rs
参考
https://github.com/kanaka/mal