ほのぼのしたエンジニアのブログ

触った技術についてまとめていくブログです。

StepByStep和訳:1.Printing on Terminal

どうもShZgZです。

 

台風本当に凄いですね。皆さんも気を付けて下さい。

引き続きRust Programming Step-by-Step · GitBookの和訳らしきものを進めていきます。

今回はコンパイラのインストールとハロワとかの画面表示系です。

 

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Terminal上にプリント

始め方

 もっとも短いRustプログラムは

fn main(){}

 これはよく下のように書かれます。

fn main() {
}

見てわかるようにこれは何も行われません。ただ"main"という名前のついた空の関数を定義しただけです。関数によって何かを行う指示の集合に名前を与える。

 

 "fn"というのは"function"の略で、"main"とは関数の名前を表します。丸括弧("()"←これのこと)には関数の引数を含むことが、波括弧("{}"←これのこと)にはステートメントを含むことができます。上の例では、引数もステートメントもありません。

 

Rustで書かれたプログラムが実行されるときは"main"関数が実行されます。"main"関数が無い場合は完全なプログラムではありません。そのときはライブラリとなります。

 

 このプログラムを実行するためには以下の操作を完了して下さい。

・Rustコンパイラとユーティリティのインストールを行う。

 コンパイラhttps://www.rust-lang.org/ja-JP/よりダウンロードすることができます。Linux, Windows, Mac OS Xx86 (32-bit) or x86-64 (64-bit)で利用可能です。それぞれのプラットフォームで"stable", "beta", "nightly"という3つのバージョンが存在します。インストールするものは"stable"バージョンをお薦めします。"stable"バージョンは最も古いですが、最もテストされ、これから変更される可能性も低いものとなっています。これらのバージョンは全てコンソールのコマンドラインより利用されます。インストール後は、インストールされたバージョンを確認するために以下のコマンドを実行して下さい。

rustc -V

(因みに私の環境では"rustc 1.20.0"が表示されます。)

・Rust練習用のファイルを保存するためのフォルダを作成(もしくは指定)し、上部の最も短いRustプログラムの内容を持ったmain.rsファイルをお好みのテキストエディタで作成して下さい。

コマンドライン上でRust練習用フォルダに移動し、以下のコマンドを実行して下さい。

rustc main.rs

実行するとすぐにプロンプト(コマンドラインでinputバーの前にある文字のことで、Windowsなら">"、それ以外なら多分"$")が表示されます。この時、"main"という名前のついたファイルが作成されています。(Windowsでは"main.exe"になっています。)実際に、"rustc"コマンドは正しく指定されたファイルのコンパイルを行いました。つまり、ソースコードを読み、対応するマシンコードを生成し、そのマシンコードを同じフォルダに保存したわけです。

コマンドライン上で、Windowsならば以下を実行して下さい。

main

その他のOSでは以下を実行して下さい。

./main

これで作成されたプログラムを実行することが出来ました。しかし、今回は何も行わないプログラムなので、またすぐにプロンプトが表示されてしまうはずです。

 

Hello, World!

それではコマンドライン上にテキストを表示する方法を見ていきましょう。前節のプログラムを以下のように変更してみましょう。

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

前のようにコンパイルし、実行すると、以下のように表示されます。

Hello, world!

新しく追加された行が"トークン"として知られる8つの構文アイテムを持つことに注目して下さい。それらを調べていきましょう。

  • print: これはRustのスタンダードライブラリに定義されたマクロ名です。
  • !: これはその前にある名前がマクロであることを示しています。このシンボルが無いと、"print"は関数を表します。しかし、Rustのスタンダードライブラリにそのような関数は無いので、コンパイルエラーとなります。マクロは関数に似た物です。(それはRustの名前に関連付いたステートメントの集合を表す。この名前を利用することで、それらのステートメントを実行することができる。)
  • (: マクロの引数リストの開始
  • ": 文字列リテラルの開始
  • Hello, world!: 文字列リテラルの内容
  • ": I文字列リテラルの終了
  • ): マクロの引数リストの終了
  • ;: ステートメントの終了

文字列リテラルの意味を調べてみましょう。

"文字列"とは「スペースや句読点を含む長さの決まった文字列」

"リテラル"とは「ソースコードに直接指定される値」

つまり、"文字列リテラル"とは「ソースコードに直接指定される長さの決まった文字列」(上の例だと"Hello, world!"←これが文字列リテラル)

 

 "println"マクロは単に受け取った引数をコマンドライン上に表示します。

 

 Rustは大文字と小文字を区別します。(大文字と小文字を区別することを英語で"case sensitive"って言うらしい)

文字列リテラルやコメント部分(Rustでコメントアウトは"// ここ行末までコメント"か"/* 囲まれてる部分コメントで、コメントを改行したかったら行頭にアスタリスク */")以外の1文字の大文字小文字を入れ替えたりすると、コンパイルエラーになったり、挙動が変わってしまう。代わりに、文字列リテラル内で同様のことを行うとコンパイルは成功するが、挙動は変わってしまうかもしれない。

例えば、

fn Main() {
}

このプログラムをコンパイルしようとすると、プログラム中に"main"関数が定義されていないとしてコンパイルエラーとなる。("main"と"Main"を区別するし、前に説明したようにプログラムには"main"関数が必ず必要だから。)

今後、指定した場合を除き、例示するコードは"main"関数内に書かれていることを想定するため、"main"関数の定義ステートメントは省略します。

 

文字列の連結を表示

下のような方法で、1つの文字列リテラルを使用する代わりに、1つのステートメントで複数の文字列リテラルを表示できる。

print!("{}, {}!", "Hello", "world");

このステートメントを"main"関数の内部に書くと、再び以下の文字列が表示される。

Hello, world!

この場合、"println"マクロは","(カンマ)で区切られた3つの引数を受け取っている。引数は全て文字列リテラルで、最初の文字列は二つの波括弧のペア("{}")を含んでいる。それらはプレースホルダーで、他2つの文字列を挿入する場所を示している。

だから、このマクロは最初の引数の後に全ての引数を調べる。そして、最初の引数の中の波括弧のペアそれぞれを他の引数と置き換える。

これは以下のC言語ステートメントと似ている。

printf("%s, %s!", "Hello", "world");

But there is an important difference. If you try to compile

しかし、ここには重大な違いがある。もし、以下のコードをコンパイルしようとすると

print!("{}, !", "Hello", "world");

you get a compilation error, as the arguments of the macro after the first one are more numerous than the placeholders inside the first argument; that is, there is some argument that doesn't have any corresponding placeholder.

最初の引数を除いた残りの引数の数が、最初の引数内部にあるプレースホルダーの数より多い(上の例だと"{}, !"の中にはプレースホルダー1つなのに、"Hello"と"world"って2つあって1つ多いよねってこと)ためコンパイルエラーとなる。つまり、対応するプレースホルダーの存在しない引数がある。

そして、もし、以下のコードをコンパイルしようとすると

print!("{}, {}!", "Hello");

上の逆で、引数より多いプレースホルダーがあるとしてコンパイルエラーとなる。つまり、対応する引数の存在しないプレースホルダーがある。

一方でC言語の方ではコンパイルエラーは起こらないが、代わりに実行時にクラッシュするかおかしな挙動を引き起こす。

 

複数行を表示

今まではただ1行を表示するプログラムを書いてきたが、ここでは以下のようにして1つのステートメントで複数行を表示する。

print!("First line\nSecond line\nThird line");

これは以下のように表示される。

First line
Second line
Third line

The sequence of characters \n, where n stands for "new line", is transformed by the compiler into the character sequence that represents the line terminator for the currently used operating system.

この文字列"\n"はコンパイラによって、以下のように変換される。

"\n" → "そのOS上で使用される行末を表す文字列"

これが与えられたら、全てのプリントステートメントに関して、ステートメントの最後で一度改行することはとても普通のことであり、Rustのスタンダードライブラリにはそのようなマクロが用意されている。

println!("text of the line");

Calling this println macro (whose name is to be read "print line"), is equivalent to calling print with the same arguments, and then outputting a line terminator. That is, that statement is equivalent to:

"println"(これは"print line"と読まれる)マクロを呼ぶことは、いくつかの引数と共に"print"を呼び、行末を表す文字列を表示することと同義である。つまり、このステートメントは以下と同様

print!("text of the line\n");

 

整数を表示

もし、整数を表示したかったら

print!("My number: 140");

もしくは

print!("My number: {}", "140");

もしくは、第2引数のクォートを除いて

print!("My number: {}", 140);

これらのステートメントは以下のように表示される。

My number: 140

最後のステートメントにおいて、第2引数は文字列リテラルではなく、整数リテラルである。

整数は文字列以外のデータ型で、"print"マクロで第1引数に含まれる対応するプレースホルダーを置き換えることができる。

事実、コンパイラソースコードに含まれる文字列"140"を10進表記の数として解釈し、それはバイナリフォーマットにおいて数との等価性を生成し、実行プログラムに保存する。

実行時には、そのプログラムはバイナリフォーマットのその数を受け取り、文字列"140"に変換し、プレースホルダーを文字列と置き換え、表示用文字列を生成し、その文字列をコマンドラインへ送る。

この手続きは例えば、以下のようなプログラムだとどうなるかを説明している。

print!("My number: {}", 000140);

the compiler generates exactly the same executable program generated before. Actually, when the source string 000140 is converted to the binary format, the leading zeros are ignored.

コンパイラは前と同様の実行プログラムを生成する。実際に、ソース文字列"000140"はバイナリフォーマットに変換され、先頭の"0"は無視される。

引数の型が異なったとしても、このステートメント

print!("{}: {}", "My number", 140);

前と同様の行を表示するだろう。ここでは1つ目のプレースホルダーは文字列リテラルに対応し、2つ目は整数リテラルに対応する。

 

コマンドラインスクリプト

上で使ったrustcコマンドは大きい欠点を持っている。その欠点とは、コード中に見つかったエラーを全て見つかった順番で表示してしますことである。もし、あなたのコードが多くの構文エラーを含み、それらを最初から最後まで修正しなければならないような時があったとします。しかし、コンパイラがそれらのエラーを出力し終えると、プロンプトのすぐ上の部分には最後に見つかったエラーが表示されており、最初のエラーへ戻るためにはスクロールしていかなければならない。

一時的な解決法として、以下のコマンドを、Unix-like terminalもしくはLinux上で実行する。

clear; rustc $* --color=always 2>&1 | more

You can put it in a script file, named, say, rs, so that to compile a file named main.rs you can type:

あなたはこれを"rs"と名付けられたスクリプトファイルに書くことにより、"main.rs"と名付けられたファイルを以下のように実行できる。

rs main.rs

This script first clears the screen, then runs the rustc compiler with all the arguments you give to it. If the compilation is successful, or if it generates less than a screenful of error messages, it behaves like a normal run of rustc.

このスクリプトはスクリーンをクリアし、全ての引数とともにrustcコンパイラを実行する。もし、コンパイルが成功したり、エラーメッセージがスクリーンに全て表示される程度の量だった場合は通常のrustcように振舞う。

一方、エラーメッセージがスクリーンを覆ってしまった場合は表示が止まり、"--More--"というメッセージがターミナルスクリーンの下部に表示される。この時、いくつかのキーを押すことができる。

  • Enterキーは1行進む。
  • スペースキーは1スクリーン分進む。
  • Qキー("quit"を表す)は、途中でエラーメッセージの表示を止め、コマンドラインに戻る。