Rustのコードを書いてみる
アプリケーションの最初のステップとして、サンプルで作られたmain.rsを書き換えてみましょう。
このコードは単純に標準入力から押されたキーの文字を読み込んで表示します。
// first_step
// 標準ライブラリの読み込み
use std::io::{self, Read};
// main 関数
fn main() {
// STDINからbyte列を読み込み
for input_byte in io::stdin().bytes() {
// 読み込んだbyteをcharに変換して束縛
let output_char = input_byte.unwrap() as char;
// 読み込んだ値を出力
println!("{}", output_char);
}
}
まず最初にコメントが書かれています。
Rustでは2重スラッシュが書かれた後はコメントとして扱われます。これは1行コメントとして機能します。また複数行向けには/*で始まり*/で終わるブロックコメントが使用できます。
続いてuseを使ってRustの標準入出力ライブラリを読み込んでいます。
このuse文は下記と等価です。
use std::io;
use std::io::Read;
std::ioを読み込むことでstdin関数を使用することが可能となります。また、std::io::Readを読み込むことでこのトレイトのbytesメソッドが使用可能になります。
次にmain関数の最初の行は、多少複雑で「キーボードから読み取れるすべてのバイト値について、それをinput_byte変数1に束縛して次のブロックを実行する」ということです。
io::stdin()を呼ぶことで標準入力からすべての値を読み込み、bytes()を呼ぶことで読み込んだデータのイテレータを取得します。
イテレータを使うと、for..in文でループを作ることができます。for..inとbytes()を組み合わせると、標準入力からbyteを変数input_byteに読み込むように要求し、読み込むbyteがなくなるまでそれを続けることになります。
for..inの後の2行は、各文字をプリントアウトし(unwrapとprintlnについては後で説明)、読み込むものがなくなったら再びio::stdin()に戻ってきます。
ともあれ実行してみましょう。(終了はCtrl+CあるいはCtrl+D)
$ cargo run
Compiling binllion v0.1.0 (/home/my_rust/binllion)
Finished dev [unoptimized + debuginfo] target(s) in 0.15s
Running `target/debug/binllion`
a # aを入力、Enter
a
b # bを入力、Enter
b
c # cを入力、Enter
c
123 # 123を入力、Enter
1
2
3
^C
実行すると、端末(ターミナル)が標準入力に接続され、キーボード入力がinput_byte変数に読み込まれます。しかし、デフォルトでは、端末はcookedモードとも呼ばれるcanonicalモード2で起動します。
このモードでは、ユーザーがEnterキーを押したときだけ、キーボード入力がプログラムに送られます。
これは多くのプログラムにとって便利です。ユーザーがテキスト行を入力し、入力が思い通りになるまでエラーを修正し、最後に Enterを押してプログラムに送信することができます。
しかし、テキストエディタのような複雑なユーザー・インターフェースを持つプログラムでは具合が悪いです。エディタでは、キーが押されるたびにそれを処理し、即座に対応できるようにしたいです。
これを実現するためにはrawモードが必要です。
幸いなことに、端末をrawモードに設定するための外部ライブラリがあります。Rustのライブラリはcrateと呼ばれています。他のプログラミング言語と同様に、機能を拡張するためにcrateを使用することができます。
qキーで終了するようにする
canonicalモードがどのように機能するかを示すために、ユーザーからqキーが押されたことを読み取ったらプログラムが終了するように変更してみます。
実行すると、qを読み込んでEnterを押した場合はfor..inのループを抜けてプログラムが終了するようになります。
// press_q
// 標準ライブラリの読み込み
use std::io::{self, Read};
// main 関数
fn main() {
// STDINからbyte列を読み込み
for input_byte in io::stdin().bytes() {
// 読み込んだbyteをcharに変換して束縛
let output_char = input_byte.unwrap() as char;
// 読み込んだ値を出力
println!("{}", output_char);
// qが入力されたらbreak
if output_char == 'q' {
break;
}
}
}
$ cargo run
Compiling binllion v0.1.0 (/home/my_rust/binllion)
Finished dev [unoptimized + debuginfo] target(s) in 0.15s
Running `target/debug/binllion`
a # aを入力、Enter
a
b # bを入力、Enter
b
c # cを入力、Enter
c
123 # 123を入力、Enter
1
2
3
q # qを入力、Enter
q
- Rustでは変数名はsnake_caseが推奨されています。 ↩︎
- 端末制御については日本語の説明だとこの辺りが詳しいです。こちらも参考になります。とりあえずはこの説明にある通りデフォルトでは行単位で送信するcanonicalモードでは即時性がないので、アプリケーションでは行単位で処理しないrawモードに切り替える必要がある、という理解でよいかと。 ↩︎
TOC
GitHubにコードをアップロードしています。
コードのコメントに書かれているfirst_stepなどをcargoコマンドに渡すと実行できます。
# Example
$ cargo run --examples first_step