LoginSignup
65
62

More than 5 years have passed since last update.

The Rust Guide (Japanese Translation):前篇 §01-14

Last updated at Posted at 2014-10-02

これはRust Guide (0.12.0 nightly) http://doc.rust-lang.org/guide.html の日本語訳です。
このページは前半§01-§14の翻訳です。後半§15-§27はhttp://qiita.com/lex_naturalis/items/dbef84b7788efbd30e21 です。

みなさん! Rustガイドへようこそ。Rustでどうやってプログラミングをするかをお知りになりたければ、ここがその場所になります。Rustは「素のハードウェア上での高水準なプログラミング」、つまりプログラミング言語というものに可能な限りで低水準のコントロールを、しかしながら、ゼロコストで高水準の抽象とともに ― というのも人間はコンピューターではないわけですから ― 提供するということにフォーカスした言語です。私達はRustが特別なものだと本気で考えていますし、みなさんにもそう思ってもらえることを望んでいます。

Rustとどう付き合っていくかを示すために、伝統的な「Hello, World!」プログラムを書くことにしましょう。その次に、現実世界でのRustプログラムとRustライブラリを書くのに有用なツールを紹介します。その後に、Rustの基本事項についてお話し、それを試してみるために小さなプログラムを書きます。それから、更に進んだ事項について学習します。

宜しいですか? でははじめましょう!

1. Rustをインストールする

Rustを使うための最初のステップはRustのインストールです! Rustをインストールする方法はたくさんありますが、最も簡単なのは、rustupスクリプトを使うことです。LinuxかMacならば、する必要があるのはこれだけです($を自分でタイプする必要はないことに注意してください。それらは各コマンドの開始地点を示しているに過ぎません):

$ curl -s https://static.rust-lang.org/rustup.sh | sudo sh

(もしcurl | sudo shが気になるとしても続きを読んでください。免責事項は下の方にあります。)

もしWindowsならば、32-bitインストーラか64-bitインストーラをダウンロードして、それを実行してください。

あなたがもしもうRustはいらないと決心したならば、私達にとっては些か悲しいことではありますが、でもそれで構いません。あらゆるプログラミング言語が万人にとって素晴らしいものであるわけではないのですから。スクリプトにひとつ引数を渡すだけで構いません:

$ curl -s https://static.rust-lang.org/rustup.sh | sudo sh -s -- --uninstall

もしWindowsインストーラをお使いなら、.exeをもう一度実行すれば、アンインストールを選択することができます。

このスクリプトはRustを更新したいときにいつでも再実行することができます。そして目下のところ、更新はしばしば行われます。Rustはまだ1.0未満の段階ですから、(*Rustを開発している)人々はあなたがごく最近のRustを使っているものと想定します。

ここで、もう一つ別の点についてお話する番です。私達が皆さんにcurl | sudo shとするようにいったことに、それにはもっともなところがあるのですが、気分を害する人もいるかと思います。実際、そうなって当然です! 基本的には、これを実行する際にあなたはRustを保守している善意の人々があなたのコンピュータをハックして悪いことを行ったりしない、ということを信頼していることになるからです。それは立派な本能です! もしあなたがそういう人々の一員だとしたら、Rustのソースからのビルドについてのドキュメントをチェックするか、公式のバイナリのダウンロードをチェックしてください。また、私達はこの方法がいつまでもRustをインストールする定番の方法であり続けるということはないようにする、と約束します。この方法は、Rustがアルファ段階にある間、みなさんに最新の状態を保ってもらうための最も容易な方法であるというだけのことです。

あ、公式にサポートされたプラットフォームにも触れて置かなければいけませんね。

Windows (7, 8, Server 2008 R2), x86 のみ
Linux (2.6.18 以降, 様々なディストリビューション), x86 及び x86-64
OSX 10.7 (Lion) 以降, x86 及び x86-64

Rustはこれらのプラットフォームに加えて、また幾つかAndroidのようなプラットフォームでも広範にテストされています。しかし、殆どのテストはこれらのプラットフォームで行われていますから、これらがRustが動作する可能性の最も高いものです。

最後に、Windowsについてコメントしておきます。RustはWindowsをリリースの際の一級市民プラットフォームであると考えています。しかし、正直なことを言えば、Windows上での経験はLinux/ OS X上での経験ほど均整のとれたものではありません。それについてはまだ作業中です! もし何かが動作しないとしたら、それはバグです。そういうことが生じたら、どうぞ我々に知らせてください。我々はどのコミットも、他のどんなプラットフォームに対するのと同じように、Windowsに対してテストしています。

もしRustを既にインストールしたならば、シェルを立ち上げて次のようにタイプしてください:

$ rustc --version

以下の様な感じの出力結果を目にするはずです:

rustc 0.12.0-pre (443a1cd 2014-06-08 14:56:52 -0700)

こうなったのなら、Rustのインストールは成功です! おめでとう!

もしそうでないとしても、助けてもらえる場所はたくさんあります。いちばん簡単なのは、irc.mozilla.orgのIRCチャンネル#rustです。Mibbit経由でアクセスできます。リンクをクリックすれば、他の「ラスタシアン Rustaceans」(これは我々が自称しているバカげたアダ名です)とチャットすることができ、我々が皆さんを助けることができるでしょう。他の重要なリソースとしては、メーリングリストや/r/rust/subredditやStack Overflowがあります。

2. Hello, World!

さてRustをインストールしたわけですから、最初のRustプログラムを書いてみましょう。どんな新しい言語であれ、最初のプログラムは"Hello, world!"というテクストを画面に表示するものにするのが伝統です。こうした単純なプログラムから始めることの良い点は、コンパイラがきちんとインストールされているかだけでなく、それがきちんと動作しているかを検証することができるということです。それに、画面に情報を表示するというというのはごくありふれた仕事でもありますから。

最初にする必要があることは、コードを書き込むファイルを作成することです。私自身は自分のホームディレクトリにプロジェクト用ディレクトリを作って、自分のプロジェクトを全部そこに収めておくのが好きです。ですが、Rustはあなたのコードがどこに置かれていようが気にはしません。

ここまで来たところで、もうひとつ触れておくべき懸念があります。このガイドは、みなさんがコマンドラインの基本的な操作に親しんでいることを想定しています。Rustはみなさんがコマンドラインについて膨大な知識の総てを知っていることを要求はしていませんが、Rust言語がよりいっそう完成された状態に到るまでは、統合開発環境(IDE)サポートはあちこち粗のあるものにとどまるでしょう。なお、Rustはテキスト編集ツールについても、コードがどこに置かれるかについても、なんら特別な要求はしません。

それはそういうこととして、プロジェクト用ディレクトリの中にあるディレクトリを作りましょう。

$ mkdir ~/projects
$ cd ~/projects
$ mkdir hello_world
$ cd hello_world

もしWindowsを使っている場合、PowerShellを利用していないなら、~は使えないでしょう。さらなる詳細についてはご利用のシェルのドキュメントを参照してください。

次に新しいソース・ファイルを作りましょう。これ以降の例では、syntax editorという表記でファイルを編集することを表すことにしますが、別にお好きなどんな方法を用いて構いません。さて、このファイルをmain.rsということにしましょう。

$ editor main.rs

Rustファイルは常に.rsという拡張子で終わります。ファイル名に複数の単語を使うときにはアンダースコアを使ってください。つまり、helloworld.rsではなくhello_world.rsとしてください。

さて、ファイルを開いたわけですが、次にこうタイプしてください:

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

ファイルを保存して、ターミナル・ウィンドウに次のようにタイプしてください:

$ rustc main.rs
$ ./main # or main.exe on Windows
Hello, world!

成功です! 何が起きたのか、詳しく見て行きましょう。

fn main() {

}

これらの行はRustの 函数 を定義しています。main函数は特別な函数です。この函数はあらゆるRustプログラムの開始地点になります。1行目が言っているのはこういうことです。「私はmainという名前の函数を定義しています。この函数は引数を取らず、また返値を返しません。」もし引数があったとすれば、それらの引数は括弧、つまり()の中に書かれることになったでしょう。また、この函数からは何の値も返さないので、返値の表記は省略しました。それについてはまた後に触れます。

この函数が波括弧に囲まれていることにも気がつくでしょう。Rustでは、これらの括弧がどんな函数の本体にも要求されます。また、関数定義と同じ行にスペースをひとつだけ空けて、開き括弧を置くのが良いコーディングスタイルだと考えられます。

次に来るのはこの行です:

    println!("Hello, world!");

この行が、我々の小さなプログラムの仕事の全てをしています。この行には重要な詳細が多数詰まっています。まず第一は、この行は、タブではなく、4つのスペースでインデントされています。あなたが選んだエディタを設定して、タブキーがタブではなく4つのスペースを挿入するようにしてください。私達は様々なエディタ用の設定サンプルを提供しています。

第二の点は、println!()という部分です。これはRust マクロ を呼び出しています。RustマクロというのはRustでメタプログラミングを行う方法です。もしこれが代わりに函数だったとしたら、println()というような見た目になったことでしょう。目下の目的のためには、この違いについて気にする必要はありません。ただ、時に!を目にすることがあり、それが普通の函数ではなくマクロの呼び出しであるということを意味しているのだとだけ知っておいてください。最後にもうひとつ。もしあなたがCのマクロを使ったことがあるとしたら、Rustのマクロはそれとは大きく違うものです。マクロを使うことを恐れないでください。最後にはその詳細に触れますが、今のところは私達のいうことを信じておいてください。

次に、"Hello, world!"文字列 string です。文字列というものはシステムプログラミング言語に於ける驚くほど込み入ったトピックですが、これは 静的に割り当てされた 文字列です。割り当ての様々な種類については後にもっとお話します。この文字列を引数としてprintln!に引き渡し、それが画面にこの文字列を表示するのです。簡単ですね!

最後に、この行はセミコロン(;)で終わっています。Rustは 式指向 の言語です。これはつまり、殆どのものが式だということです。この;は、この式がそこで終了しており、新しい式が始まるところであるということを示すために使われています。Rustのコードは殆どの行が;で終わります。これについて立ち入ってはこのガイドの後のほうで扱います。

最後に、実際にプログラムを コンパイル して 実行 しましょう。ソースファイルの名前を渡せば、コンパイラ rustcでコンパイルすることができます。

$ rustc main.rs

もしCC++のバックグラウンドがあれば、これはgccとかclangと似たようなものに見えるでしょう。Rustは実行可能なバイナリファイルを出力します。このファイルをlsで確認してみましょう:

$ ls
main  main.rs

或いはWindowsであれば:

$ dir
main.exe  main.rs

いまや2つのファイルがあることになります。.rs拡張子がついたソースコードと、実行可能ファイル(Windowsではmain.exeで他ではmain)です。

$ ./main  # or main.exe on Windows

上のようにすると、ターミナルにHello, world!というテクストが表示されます。

もしあなたがRuby, Python, JavaScriptのような動的に型付けされた言語からやってきたのであれば、コンパイルと実行という2つのステップが分離しているのに慣れていないかもしれません。Rustは実行時前にコンパイルされる言語です。つまり、プログラムをコンパイルしてそれを誰かにあげることができるわけですが、そのときにもらった相手はRustをインストールしている必要がありません。もしあなたが.rbとか.pyだとか.jsだとかいったファイルをあげるとすると、相手はRuby/Python/JavaScriptをインストールしている必要がありますが、しかし、あなたのプログラムをコンパイルして実行するのにひとつのコマンドしかいらないで済みます。言語をデザインするに際しては、あらゆるものがトレード・オフの関係になります。RustはRustなりの選択をしたのです。

おめでとうございます! みなさんはもう公式にRustのプログラムを書いてしまったわけですから、Rustプログラマであるということになります。ようこそようこそ。

次に、みなさんを、現実世界でのRustプログラムを書くのに用いられるもうひとつ別のCargoというツールにご紹介しましょう。単にrustcだけをつかうというのは、単純なことをするには結構なものですが、プロジェクトが成長するに連れて、あなたがその全てのオプションを管理する手助けをしてくれたり、あなたのコードを他の人々や他のプロジェクトとシェアするのを簡単にしてくれたりするものが欲しくなるでしょう。

3. Hello, Cargo!

CargoはRust信奉者が自分のRustプロジェクトを管理する助けとして使うツールです。Cargoは現在のところ、ちょうどRustがそうであるように、アルファ段階であり、なおもって作業が進行中です。しかしながら、多くのRustプロジェクトにとって既に充分使うのに適したものであり、Rustプロジェクトは最初からこのツールを使うものと想定されています。

Cargoが管理するのは3つのものです。あなたのコードのビルド、そのコードが必要とする依存関係ライブラリのダウンロード、それらのビルド、です。最初のうちは、あなたのプログラムはまったく依存関係を持っていません。ですから、Cargoの機能の内で最初のものだけを使うことになります。最終的には、もっと多くのものが付け加わります。Cargoを使ってプロジェクトを始めれば、後で付け加えるのも簡単になるでしょう。

Hello WorldのプログラムをCargoに変換しましょう。Cargoを使いはじめるために最初にする必要があるのは、Cargoのインストールです。幸いなことに、Rustをインストールするために先に実行したスクリプトはデフォルトでCargoを含んでいます。もしなにか他の方法でRustをインストールしたのであれば、インストールについての詳細な指示についてCargoのREADMEをチェックするのがよいでしょう。

プロジェクトをCargo化するには、2つのことをする必要があります。Cargo.tomlという設定ファイルを作ることと、ソース・ファイルを適切な場所に置くことです。まずはそちらからやってみましょう。

$ mkdir src
$ mv main.rs src/main.rs

Cargoは、あなたのソース・ファイルがsrcというディレクトリの中に置かれていることを期待します。トップレベルはREADMEとかライセンス情報とか、なんであれあなたのコードに関係していないものを置くためにとっておくのです。Cargoはプロジェクトを整理整頓しておくのを助けてくれます。全てのものにそのための場所があり、全てのものがそれ自身の場所に収まるのです。

次に設定ファイルです:

$ editor Cargo.toml

名前を正しくつけるように注意してください。Cは大文字でなければいけません!

このファイルの中に次のように書きます:

[package]

name = "hello_world"
version = "0.0.1"
authors = [ "Your name <you@example.com>" ]

[[bin]]

name = "hello_world"

このファイルはTOML形式で書かれています。TOMLの説明はTOML自身にやってもらいましょう:

TOMLは自明な意味論を有し、そのお蔭で読みやすい、ミニマルな設定ファイル形式であることをその狙いとしています。TOMLはハッシュ・テーブルに曖昧性なしに写像(map)できるようにデザインされています。広範にわたる様々な言語で、TOMLをデータ構造へとパーズすることは容易なはずです。TOMLはINIファイル形式にとてもよく似ていますが、それに加えてステキな特徴が幾つかあります。

ともかく、このファイルには2つの テーブルpackagebinがあります。前者はCargoにあなたのパッケージについてのメタデータを伝えるものです。後者はCargoに、私達がライブラリではなくバイナリをビルドしたいと思っていること(両方することもできるのですが!)と、更にそれがどのような名前を付けられるかを、伝えるものです。

このファイルが準備できたなら、ビルドの用意が出来ました! 次のようにしてみましょう:

$ cargo build
   Compiling hello_world v0.0.1 (file:///home/yourname/projects/hello_world)
$ ./target/hello_world
Hello, world!

バーン! プロジェクトをcargo buildでビルドし、./target/hello_worldでそれを実行しました。いまのところまだ、単純にrustcを使う場合に比べて大したものが手に入ったわけではありません。しかし、将来、つまりプロジェクトがひとつだけでなく複数のファイルを持つようになった時のことを考えてみてください。rustcを2回呼んで、それに大量のオプションを渡して全部を一緒にビルドするように命令しなければいけません。Cargoがあれば、プロジェクトが成長するに連れても、 ただcargo buildするだけで正しく作業をしてくれるのです。

CargoがCargo.lockという新しいファイルを作成したことにも気がついたと思います。

[root]
name = "hello_world"
version = "0.0.1"

Cargoはこのファイルを、あなたのアプリケーションの依存関係を把握するために使用します。ちょうどいまのところは、依存関係がないので、このファイルはちょっとスカスカしていますね。みなさんは自分ではこのファイルをいじる必要はありません。それの取り扱いはCargoに任せておいてください。

以上です! Cargoを使ってhello_worldが無事にビルドできました。このプログラムは単純ではありますが、あなたがRust人生の残りこれからずっと使うだろうホンモノのツールを使っているのです。

さて、ツールはこれでマスターしたわけですから、実際にRust言語そのものについてもっと学ぶことにしましょう。これらは、Rustと過ごす残りの時間を通じてずっと役に立つだろう基本事項です。

4. 変数の束縛

最初に学ぶことになるのは「変数束縛」です。見た目はこのような感じです:

let x = 5i;

多くの言語では、これは「変数 variable」と呼ばれています。しかし、Rustの変数束縛には幾つか隠しダネがあるのです。Rustには「パターン・マッチング」という極めて強力な機能があります。詳細は後に触れることにしますが、ともあれ、let式の左辺は単なる変数名ではなく、完全なパターンになっています。これがどういうことかというと、次のようなことができるのです:

let (x, y) = (1i, 2i);

この式が評価されたあとには、x1に、y2になります。パターンは本当に強力なものなのですが、今のところ扱うことができるのはこのくらいです。ここから先に進んでいく際に、このことを心に止めておいてください。

ところで、これ以降の例では、数が整数であることをiによって表すことにします。

Rustは静的に型付けされた言語です。ということはつまり、前もって型を指定しなければなりません。ならば、なぜ最初のHello, Worldの例はコンパイルできたのでしょうか。ふむ、それはRustには「型推論」と呼ばれるものがあるからなのです。もし何かがどんな型を持つのかをRustが判別できるのならば、Rustはみなさんにわざわざそれをタイプして明示してくれとは要求しません。

しかし、もしそうしたいなら、型を加えることはできます。型はコロン(:)につづけて書きます:

let x: int = 5;

教室のみんなの前でこれを声に出して読んでみて、と言われたならば「xintという型を持ち、値が5であるような束縛です。」ということになります。

デフォルトでは、変数は 変更不能 immutable です。次のようなコードはコンパイルできません:

let x = 5i;
x = 10i;

このコードは次のようなエラーになるでしょう:

error: re-assignment of immutable variable `x`
     x = 10i;
     ^~~~~~~

もし変更可能な束縛が欲しければ、mutを使ってください:

let mut x = 5i;
x = 10i;

変数束縛がデフォルトで不変であるということには、複数の理由があります。しかし、Rustの主要な関心、つまり安全性を通してそれを見てみることはできます。仮に、mutと言い忘れたとしましょう。そうすると、コンパイラはそれを捕捉して、変更するつもりがなかったはずのものを変更してしまいましたよ、と教えてくれるのです。もし変数束縛がデフォルトで変更可能だったとしたら、コンパイラはあなたにこれを通知することができなかったことでしょう。もしあなたが 本当に 変更を意図していたのだとしたら、解決策は実に簡単です。mutを付け加えればいいのです。

可能である場合に変更可能な状態というものを回避することには他にも十分な理由があります。しかし、それはこのガイドの範囲を超えてしまいます。一般的にいって、束縛の明示的な変更というのはしばしば回避することができるものですし、それゆえ、Rustではそうすることが望ましいのです。とはいうものの、時には束縛の変更が必要とするものだということもありますから、絶対的に禁止されてはいないのです。

束縛の話にもどりましょう。Rustの変数束縛は、もうひとつ他の言語と違った特徴を備えています。変数束縛は、それを使う前にある値で初期化されていなければなりません。もし次のようにしてみたら……

let x;

……エラーになります。

src/main.rs:2:9: 2:10 error: cannot determine a type for this local variable: unconstrained type
src/main.rs:2     let x;
                      ^

しかしながら、これに型を補充するとコンパイルできます:

let x: int;

試してみましょう。src/main.rsを次のように変更してください:

fn main() {
    let x: int;

    println!("Hello world!");
}

コマンドラインでcargo buildをつかってビルドできるはずです。警告はでますが、それでもなお、"Hello, world!"と表示されるでしょう。

   Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world)
src/main.rs:2:9: 2:10 warning: unused variable: `x`, #[warn(unused_variable)] on by default
src/main.rs:2     let x: int;
                             ^

Rustは、変数束縛をまったく利用していないと警告してきます。しかし、まさに全く利用していないわけですから、何の害も何の誤りもありません。しかしながら、このxを実際に利用しようとするとなると、話は別です。やってみましょう。プログラムを変更して次のようにしてください:

fn main() {
    let x: int;

    println!("The value of x is: {}", x);
}

で、ビルドしてみましょう。次のようなエラーが出るはずです:

$ cargo build
   Compiling hello_world v0.0.1 (file:///home/you/projects/hello_world)
src/main.rs:4:39: 4:40 error: use of possibly uninitialized variable: `x`
src/main.rs:4     println!("The value of x is: {}", x);
                                                    ^
note: in expansion of format_args!
<std macros>:2:23: 2:77 note: expansion site
<std macros>:1:1: 3:2 note: in expansion of println!
src/main.rs:4:5: 4:42 note: expansion site
error: aborting due to previous error
Could not compile `hello_world`.

Rustは、初期化されてない値を使用させてくれません。ともかく次に、上でprintln!に追加されたものについてお話しましょう。

表示する文字列に2つの波括弧(つまり{}ですが、これはヒゲと呼ばれたりします)を含めると、Rustはこれを、あるなんらかの種類の値を内挿せよという要求として解釈します。 文字列の内挿 というのは計算機科学の用語で、「文字列の中に貼りこむ」ということを意味します。カンマ,とそれからxを付け加えることで、xが内挿したい値だということを示します。このカンマは函数とマクロに引き渡す引数を、ひとつだけでなく複数渡す場合に、分離するために用いられています。

単に波括弧を使う場合には、Rustはその値の型をチェックして、それが有意味なものになるように値を表示しようとします。もし、より詳細にフォーマットを指定したいならば、利用できるオプションが多数ありますが、いまのところはデフォルトにしたがっておきましょう。整数を表示するというのはそれほど込み入った話ではありませんから。

5. If

Rustがどのようにif形式を取り込んでいるかは、特に複雑というわけではありませんが、より伝統的なシステム言語よりは、動的に型付けされた言語に見られるようなifのそれに遥かに似ています。そのニュアンスをしっかり把握してもらうために、それについてお話ししましょう。

ifは、「分岐 branch」という、より一般的な概念の特定的な一形式です。「分岐 branch」という名前は、木の「枝 branch」から来ています。つまり、そこでの選択に応じて複数の経路が取られうるような、決定地点のことをいいます。

ifの場合には、2つの経路につながる1つの選択があるわけです:

let x = 5i;

if x == 5i {
    println!("x is five!");
}

もしxの値を何か別のものに変更すると、このprintln!の行はテキストを表示しなかったことでしょう。より仔細に言えば、ifの後の式がtrueに評価されるならばブロックが実行され、他方でもしそれがfalseなら、ブロックは実行されないのです。

もしfalseの場合に何かが起きて欲しいのならば、elseを使います:

let x = 5i;

if x == 5i {
    println!("x is five!");
} else {
    println!("x is not five :(");
}

これはどれもごく標準的なものです。しかしながら、こういうこともできるのです:

let x = 5i;

let y = if x == 5i {
    10i
} else {
    15i
};

これは次のように書くこともできます(そして恐らくそう書くべきです):

let x = 5i;

let y = if x == 5i { 10i } else { 15i };

Rustについて、2つの興味深い点が、ここから明らかになります。Rustは式に基づく言語であるということ、そしてセミコロンが、他の「波括弧とセミコロン」に基づく言語でのそれと異なったものであること、です。この両者は関係しています。

5.1 式 vs. 文

Rustは第一義的には式に基づく言語です。Rustには文は2種類しかなく、残りの全ては式になります。

その違いはなんでしょうか。式は値を返しますが、文は値を返しません。多くの言語では、ifは文であり、let x = if ...は意味をなしません。しかし、Rustでは、ifは式であり、つまり、値を返します。なので、この値を束縛の初期化に使うことができるのです。

このことについて言っておくと、束縛はRustにある2つの文の種類のうち第1のものです。正しい名称は 宣言文 といいます。これまでのところ、letは我々が目にした唯一の種類の宣言文です。そこで、更に幾つかの宣言文についてお話しましょう。

変数束縛が、単に文としてではなく、式として書ける言語もあります。たとえばRubyのように:

x = y = 5

しかしながら、Rustではletを束縛の導入に使うことは式ではありません。次のようにするとコンパイル時にエラーを生じることになります:

let x = (let y = 5i); // expected identifier, found keyword `let`

ここでコンパイラが言っているのは、コンパイラは式の開始が見つかることを期待していたのだがletが見つかってしまい、そしてletは式ではなく文を始めることしかできないぞ、ということです。

既に束縛された変数(たとえば y = 5i)に対する割当は、その値が特に有用であるというわけではないものの、なお依然として式である、ということに注意してください。割当が割当された値へと評価される(たとえば先の例では5i)Cとは違って、Rustでは割当の値はユニット型の値()になります(これについては後に触れます)。

Rustに於ける文の種類の第2は、 式文 です。その目的は任意の式を文に転換することにあります。実用上の点から言えば、これはRustの文法は文が他の文に後続することを期待する、ということです。つまり、複数の式をお互いから分離するためにはセミコロンを使うということです。このことは、各行の終端にセミコロンを使うことを要求する他の言語の殆どとRustの見かけがよく似ているということを意味します。Rustで目にする殆どどの行の終端にもセミコロンを目にすることになるでしょう。

ここで「殆ど」と言わなければならなかったのはどんな例外のせいでしょうか? みなさんは既にそれを目にしています。このコードです:

let x = 5i;

let y: int = if x == 5i { 10i } else { 15i };

yは整数であって欲しいということを明示的に指定するために、yに型註釈を加えたことに注意してください。

これは次のものとは同じではありません。こちらはコンパイルできません:

let x = 5i;

let y: int = if x == 5i { 10i; } else { 15i; };

1015の後にあるセミコロンに注意してください。Rustは次のようなエラーを示します:

error: mismatched types: expected `int` but found `()` (expected int but found ())

整数を期待していたのに、出てきたのは()です。()は「ユニット」と発音され、Rustの型システムに於けるある特別な型です。Rustでは()int型の変数に対する有効な値とはなりません。()という型の変数に対して有効な値は唯一()のみであり、この値は特に役に立ちません。文は値を返さない、といったことを覚えていますか? よろしい、それがこの場合におけるユニットの目的です。セミコロンは任意の式を文に転換し、その式の値を投げ捨てて、代わりにユニットを返すのです。

Rustのコードで、行末にセミコロンを目にしないもうひとつの時があります。それについて説明するには、次に扱う概念、つまり函数、が必要です。

6. 函数

これまでに函数をひとつは目にしていますよね。つまりmain函数です:

fn main() {
}

これは可能な限り最も単純な函数宣言です。既に述べたように、fnは「これは函数です」と告げています。これに名前が続き、この函数は引数を取りませんから括弧が幾つか、そして本体を示すために波括弧が幾つか、続きます。次のものはfooという名前の函数です:

fn foo() {
}

引数を取る場合にはどうするのでしょう? 数を表示する函数はこうなります:

fn print_number(x: int) {
    println!("x is: {}", x);
}

print_numberを使った完全なプログラムはこうなります:

fn main() {
    print_number(5);
}

fn print_number(x: int) {
    println!("x is: {}", x);
}

見ての通り、函数の引数はletと良く似ています。つまり、引数の名前に型をつけるのです。型はコロンの後に書きます。

2つの数を一緒に加算して表示する完全なプログラムはこうです:

fn main() {
    print_sum(5, 6);
}

fn print_sum(x: int, y: int) {
    println!("sum is: {}", x + y);
}

引数はカンマで分離します。これは函数を宣言する時だけでなく、函数を呼び出すときにもそのようにします。

letとは違い、函数の引数の型は宣言しなければいけません。ですから、次のようにすると上手く行きません:

fn print_number(x, y) {
    println!("x is: {}", x + y);
}

これは次のようなエラーになります:

hello.rs:5:18: 5:19 error: expected `:` but found `,`
hello.rs:5 fn print_number(x, y) {

この言語デザインは意図的になされたものです。プログラムの全面にわたって型推論をすることは可能ではありますが、Haskellのようにそうした型推論を持っている言語では、型を明示することがベスト・プラクティスであるとされることがしばしばです。私達は、函数には型を宣言するように強制しつつ函数本体の内部では型推論を許す、というのが全面的な型推論と型推論なしとの間の優れたスイートスポットであるという見解に達しました。

函数から値を返すにはどうしたらいいのでしょうか? 整数に1を加える函数はこうなります:

fn add_one(x: int) -> int {
    x + 1
}

Rustの函数はちょうどひとつの値を返します。その型は「矢印」のあとに宣言します。この「矢印」はダッシュ(-)に「大なり」記号(>)を続けて書きます。

上の例にセミコロンがないことに気がついたことと思います。もしそれを付け加えてしまうと:

fn add_one(x: int) -> int {
    x + 1;
}

エラーになります:

error: not all control paths return a value
fn add_one(x: int) -> int {
     x + 1;
}

note: consider removing this semicolon:
     x + 1;
          ^

セミコロンと()について以前に話をしたのを覚えていますか? この函数はint を返すと宣言しているのですが、セミコロンがあると代わりに()を返してしまうことになるのです。Rustは、これが恐らく私達のやりたいことではない、と理解して、このセミコロンを取り除くことを 提案します。

これは前に出てきたif文に大変よく似ています。つまり、ブロック({})の結果は式の値になるのです。Rubyのような、他の式指向の言語もこれと同じように動作しますが、システムプログラミング言語界ではこれはあまり見ません。人々は、最初にこのことについて学んだ時には、これがバグを招き寄せるのではないかと想定します。しかし、Rustの型システムはとても強力で、ユニットはそれ自体でひとつのユニークな型なので、呼び出し元へ返ろうとする位置でセミコロンを加えたり除いたりすることがバグを引き起こす、というような問題が起きたことはまったくありません。

しかし、もっと早い位置で呼び出し元に返るにはどうしたいいのでしょうか? Rustにはそのためのキーワード、returnがあります。

fn foo(x: int) -> int {
    if x < 5 { return x; }

    x + 1
}

函数の最終行でreturnを使っても動作はしますが、しかしこれはよくないスタイルだと考えられています:

fn foo(x: int) -> int {
    if x < 5 { return x; }

    return x + 1;
}

函数を定義するには他にも方法があるのですが、それにはまだ学んでいない機能が含まれていますので、今のところそれはそのときにとっておきましょう。

7. コメント

函数が手に入ったわけですから、次にコメントについて学ぶというのが良い考えでしょう。コメントは、あなたのコードについて説明する助けとして他のプログラマに残す注記のことです。

Rustには、みなさんが大事にしなければならない二種類のコメントがあります。行コメントとドキュメント・コメントです。

// 行コメントはなんであれ'//'の後に書かれたもので、その行の行末までを範囲とします。

let x = 5i; // this is also a line comment.

// もし何かについて長い説明をする必要があるなら、行コメントをお互いに隣り合わせて
// 書くことができます。読みやすくするために、// とコメントとの間にはスペースを空けましょう。

残ったもうひとつの種類のコメントはドキュメント・コメントです。ドキュメント・コメントには//ではなく///を使います。その中ではMarkdown記法が使えます。

/// `hello` は与えられた名前に基づいてパーソナライズされた挨拶を表示する函数である。
///
/// # 引数
///
/// * `name` - 挨拶したい人の名前
///
/// # 使用例
///
/// ```rust
/// let name = "Steve";
/// hello(name); // "Hello, Steve!" と表示する
/// ```
fn hello(name: &str) {
    println!("Hello, {}!", name);
}

ドキュメント・コメントを書く際には、どの引数についても、どの返り値についても、それごとのセクションを設け、また使用法について幾つかの例を挙げると、本当に本当に、役にたちます。

rustdocというツールを使って、これらのドキュメント・コメントからHTMLドキュメントを生成することができます。rustdocについてはモジュールの話をするところまでいってから触れます。というのも、一般的にいって、ドキュメントをエクスポートしたいのは、あるひとつのモジュール全体に対してのことだからです。

8. 複合データ型

Rustは多くのプログラミング言語と同様に、多数の様々な組み込みのデータ型を備えています。既に整数と文字列でちょっとした簡単な作業をしましたね。しかし、次に、データを保持するための、より込み入ったさらなる方法についてお話しましょう。

8.1. タプル

最初に話をする複合データ型は タプル tuple と呼ばれるものです。タプルは固定サイズの順序リストです。こんな感じです:

let x = (1i, "hello");

括弧とカンマによって、この長さ2のタプルは形成されています。同じコードですが、型註釈付きのものはこうなります:

let x: (int, &str) = (1, "hello");

見ての通り、タプルの型はまさにタプルと同じように見えます。ただし、それぞれの位置に、値ではなく型名が入ります。注意深い読者なら、タプルが異種的(heterogenous)であることに気がつくでしょう。つまり、このタプルにはint&strが入っています。&strという型はまだ見たことがないでしょうが、文字列の詳細については後でお話しましょう。システムプログラミング言語に於いては、文字列は他の言語の場合に比べて少しばかり複雑なものなのです。いまのところは、ただ&strを「文字列スライス」と読んでおいてください。それ以上のことはすぐ後に学習します。

タプルのフィールドには *構造分解のlet * というものを使ってアクセスします。これがその例です:

let (x, y, z) = (1i, 2i, 3i);

println!("x is {}", x);

前に、let文の左辺は単に束縛割当をする以上の強力な働きをすると述べたことを覚えていますか? これがそれです。letの左辺にはパターンをおくことができます。もしこのパターンが右辺にマッチすると、複数の束縛を一度に割当てすることができるのです。この場合だと、letはタプルを「構造分解 destructure」ないし「解体 break up」して、その破片を3つの束縛に割当しているわけですね。

このパターンというやつは非常に強力で、後にもっと繰り返して出てきます。

タプルについて言っておくべき最後の点は、タプルが等しいのは、そのアリティ(項数)、型、値、の全てが等しい時に限られる、ということです。

let x = (1i, 2i, 3i);
let y = (2i, 3i, 4i);

if x == y {
    println!("yes");
} else {
    println!("no");
}

値が等しくないので、このコードはnoを表示します。

タプルのもうひとつ別の使い方は、函数から複数の値を返すときに使うというものです。

fn next_two(x: int) -> (int, int) { (x + 1i, x + 2i) }

fn main() {
    let (x, y) = next_two(5i);
    println!("x, y = {}, {}", x, y);
}

Rustの函数はひとつの値しか返すことができないのですが、タプルはたまたま2つの値から出来上がっているだけで値としてはひとつの値です。また、この例を見ると、函数が返してきたパターンをどのように分解すればいいかもわかりますね。

タプルは非常に単純なデータ構造で、あなたが欲しいデータ構造だということはそこまで多くありません。続けて、タプルの兄貴分である構造体(structs)に行きましょう。

8.2. 構造体

構造体は、タプルと同様に、「レコード型」のもうひとつ別の形態です。しかし、次のような違いがあります。構造体はそれが含む各要素に名前を与えます。これは「フィールド」とか「メンバー」と呼ばれます。見てみましょう:

struct Point {
    x: int,
    y: int,
}

fn main() {
    let origin = Point { x: 0i, y:  0i };

    println!("The origin is at ({}, {})", origin.x, origin.y);
}

たくさんのことが続けて行われているので、分解してみましょう。まず、structというキーワードで構造体を宣言し、それに構造体の名前が続きます。命名規約によって、構造体の名前は大文字で始まり、キャメルケースで書かれます。たとえば、Point_In_SpaceではなくPointInSpaceと書きます。

letによってこの構造体のインスタンスを作成するのはいつも通りですが、key: valueという形式の構文を使って、各フィールドに値をセットします。順番は元々の構造体の宣言と同じでなくても構いません。

最後に、フィールドには名前がありますから、ドット記法でフィールドにアクセスします。つまり、origin.xです。

構造体の中の値は、Rustでの他の束縛と同様に、変更不能です。しかしながら、mutを使えば変更可能にすることもできます:

struct Point {
    x: int,
    y: int,
}

fn main() {
    let mut point = Point { x: 0i, y:  0i };

    point.x = 5;

    println!("The point is at ({}, {})", point.x, point.y);
}

これはThe point is at (5, 0)と表示します。

8.3. タプル構造体とNewtype

Rustにはもうひとつ、タプルと構造体の間のハイブリッドのようなデータ型があります。タプル構造体には名前がありますが、フィールドには名前がありません。

struct Color(int, int, int);
struct Point(int, int, int);

この両者は、たとえ同じ値を持っている場合でも、等しくなりません。

let black  = Color(0, 0, 0);
let origin = Point(0, 0, 0);

殆どの場合、タプル構造体ではなく構造体を使うほうがよいでしょう。ColorPointは代えて次のように書くことができます:

struct Color {
    red: int,
    blue: int,
    green: int,
}

struct Point {
    x: int,
    y: int,
    z: int,
}

こうすると、タプルの中の位置ではなくて、きちんと名前が使えます。良い名前というものは重要であり、構造体であればきちんと名前が付けられるのです。

しかしながら、タプル構造体がとても役に立つ場合があります。それは、要素がひとつしかないタプル構造体です。これは「newtype」と呼ばれます。というのも、ある型について同義な別名である新たな型を作らせてくれるからです。

struct Inches(int);

let length = Inches(10);

let Inches(integer_length) = length;
println!("length is {} inches", integer_length);

見ての通り、内部の整数型は構造分解のletで取り出すことができます。

8.4. 列挙体

最後に、Rustには「直和型 sum type」である 列挙体 enum があります。列挙体は信じられないくらい有用なRustの機能で、標準ライブラリのいたるところで使われています。以下の例は、Rust標準ライブラリで提供されているものです:

enum Ordering {
    Less,
    Equal,
    Greater,
}

Ordering型の値は、Less, Equal, Greaterのいずれかひとつにしかなれません。次の例を見てください:

fn cmp(a: int, b: int) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

fn main() {
    let x = 5i;
    let y = 10i;

    let ordering = cmp(x, y);

    if ordering == Less {
        println!("less");
    } else if ordering == Greater {
        println!("greater");
    } else if ordering == Equal {
        println!("equal");
    }
}

cmpは2つのものを比較してOrderingを返す函数です。2つの値が、より大きいか、より小さいか、それとも等しいかに応じて、LessGreaterEqualかのいずれかを返すわけです。

変数orderingOrderingという型を持っており、それゆえ3つの値のいずれかを保持しています。ですから、一連のif/elseによる比較で、それがどの値かをチェックすることができます。

しかしながら、if/elseを繰り返して比較を行うのはごく煩雑になります。Rustには、これをより読みやすくするだけでなく、場合分けを漏らさないことを保証してくれる機能があります。しかしながら、それに行く前に、更にもう一種類の列挙体を見てみましょう。今度は値を伴った列挙体です:

この列挙体には2つのヴァリアントがありますが、その一方が値を持っています:

enum OptionalInt {
    Value(int),
    Missing,
}

fn main() {
    let x = Value(5);
    let y = Missing;

    match x {
        Value(n) => println!("x is {:d}", n),
        Missing  => println!("x is missing!"),
    }

    match y {
        Value(n) => println!("y is {:d}", n),
        Missing  => println!("y is missing!"),
    }
}

この列挙体は、あるかもしれないしないかもしれないintというものを表現しています。見つからない場合、つまり、Missingの場合には値がありません。しかし、Valueの場合には、値があります。しかしながら、この列挙体はintに特定されてしまっています。これを、どんな型でも使えるようにすることができるのですが、それはまだ先の話です!

列挙体の中には任意の数の値を入れることができます。

enum OptionalColor {
    Color(int, int, int),
    Missing
}

値を伴った列挙体はとても有用です。しかし、既に触れたように、異なった型を渡るジェネリックなものであるときに、更にいっそう有用になります。しかしながら、ジェネリクスに行く前に、まずは、これまで書いてきたこの巨大なif/else文をどうやって手直しするかについてお話しましょう。それはmatchを使って行うのです。

9. Match

単純なif/elseでは充分でないことがしばしばあります。というのも、可能なオプションが2つのとどまらないことがしばしばあるからです。else部分の条件が信じられない程込み入ったものになることもあります。では、どういう解決策があるでしょうか?

Rustにはmatchというキーワードがあり、これはこんがらがったif/elseのまとまりを、より強力なもので置き換えさせてくれるのです。見てみましょう:

let x = 5i;

match x {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    4 => println!("four"),
    5 => println!("five"),
    _ => println!("something else"),
}

matchは式を取り、その値に基づいて分岐します。分岐のそれぞれの「肢」は、val => explessionという形を取ります。もし値がマッチすると、その肢の式が評価されます。これにmatchという名前がついているのは、「パターン・マッチング」という用語によるもので、matchはこのパターン・マッチングの実装なのです。

で、これのなにがそんなに大きな利点だというのでしょうか? よろしい、幾つかあります。なによりもまず、matchは網羅的チェックをしてくれます。最後にある、アンダースコア(_)のついた肢が見えますか? もしこれを取り除いてしまうと、Rustは次のようなエラーを表示します:

error: non-exhaustive patterns: `_` not covered

言い換えると、Rustが言おうとしているのは、我々がある値を失念したのではないか、ということです。xは整数ですから、Rustはそれが数多ある様々な値を取りうるということを知っています。たとえば6iです。しかし、_がなければ、それにマッチする肢がありません。だから、Rustはコンパイルを拒絶するのです。_は「なんでもござれ」のような肢です。もし他のどの肢もマッチしないと、_の肢がマッチします。この「なんでもござれ」な肢があるので、xが取りうるどんな値に対しても肢があることになり、このプログラムはコンパイルできるようになるのです。

match文は、また列挙体の構造分解もしてくれます。列挙体の節で出てきた次のコードを覚えていますか?

fn cmp(a: int, b: int) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

fn main() {
    let x = 5i;
    let y = 10i;

    let ordering = cmp(x, y);

    if ordering == Less {
        println!("less");
    } else if ordering == Greater {
        println!("greater");
    } else if ordering == Equal {
        println!("equal");
    }
}

このコードはmatchとして書き換えることができます:

fn cmp(a: int, b: int) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

fn main() {
    let x = 5i;
    let y = 10i;

    match cmp(x, y) {
        Less    => println!("less"),
        Greater => println!("greater"),
        Equal   => println!("equal"),
    }
}

このヴァージョンはノイズが大変に少なくなっており、Orderingの可能なヴァリアントの全てをカバーしていることを確実にするために、網羅的チェックをしてくれます。if/elseのヴァージョンでは、例えばもしGreaterの場合を忘れた時にも、プログラムは喜んでコンパイルされたことでしょう。他方で、matchGreaterを忘れた場合には、コンパイルできません。Rustは分岐の全てのベースを確実にカバーする手助けをしてくれるのです。

また、matchは式ですから、let束縛の右辺で使うことができます。先のコードは次のように実装することができます:

fn cmp(a: int, b: int) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

fn main() {
    let x = 5i;
    let y = 10i;

    let result = match cmp(x, y) {
        Less    => "less",
        Greater => "greater",
        Equal   => "equal",
    };

    println!("{}", result);
}

この場合には、作る必要がないところでわざわざ一時的な文字列を作っているだけなので、大した意味はありませんが、しかしこれがよいパターンであることがしばしばあります。

10. 繰り返し

ループは、まだ私達が学んでいない、Rustに於ける最後の基本構造です。Rustには、forwhileという2つの主要なループ構造があります。

10.1 for

forループは、ある特定回数のループをするために用いられます。しかしながら、Rustのforループは、他のシステム言語とは些かことなった動作をします。Rustのforループは次のような「C言語スタイル」のforループとは、違った見た目をしています。


for (x = 0; x < 10; x++) {
    printf( "%d\n", x );
}

Rustでは、Cとは違い、このようになります:

for x in range(0i, 10i) {
    println!("{:d}", x);
}

少しばかり抽象的に表現すると:

for var in expression {
    code
}

となります。

この式は イテレータ になるのですが、それについてはこのガイドの後の方で、詳細に話しをします。イテレータは、あるひと連なりの要素を返してきます。各要素がループの反復1回分になります。その値がvarという名前に束縛され、この束縛はループの本体に於いて有効になります。本体が1回終わると、次の値がイテレータから取り出され、もういちどループすることになります。値がそれ以上なくなると、forループは終了します。

この例では、rangeが、開始位置と終了位置を取る函数で、これらの値を渡るイテレータを返します。ただし、上界は除外的なので、このループは0から9までを表示しますが、10は表示しません。

RustにC言語スタイルのforループがないのはわざとです。ループの各要素を手ずから制御するのは、熟練のCプログラマにとってすら、複雑であり、エラーを招きがちです。

forについては、このガイドの後の方でイテレータに触れる際にもっとお話しましょう。

10.2. while

Rustに於ける残りのもうひとつのループ構造が、whileループです。こんなかんじです:

let mut x = 5u;
let mut done = false;

while !done {
    x += x - 3;
    println!("{}", x);
    if x % 5 == 0 { done = true; }
}

whileループは何回ループする必要があるかがわからない時に、正しい選択になります。

無限ループが必要なときには、次のように書こうという気になってしまうかもしれません:

while true {

Rustにはこのようなケースを扱うための専用のキーワードloopがあります:

loop {

Rustの制御フロー分析は、この構造をwhile trueとは異なった取り扱いをします。というのも、それが常にループするということがわかっているからです。これがどういうことかの詳細を理解することは、この段階ではそれほど重要というわけではありません。しかし、一般的に言って、コンパイラに与える情報が多ければ多いほど、コンパイラは安全性とコード生成に関して、より上手く振る舞うことができます。ですから、無限にループしようという時には、いつでもloopの方を使うようにしてください。

10.3. 反復を早期に中断する

先のwhileループを見てみましょう:

let mut x = 5u;
let mut done = false;

while !done {
    x += x - 3;
    println!("{}", x);
    if x % 5 == 0 { done = true; }
}

いつループから抜け出すべきかを知るために、doneというmutなブール型の変数束縛をそれ専用に当てなければいけませんでした。Rustには、反復を調整する助けになる2つのキーワード、breakcontinueがあります。

この例の場合、breakを使えば、ループをもっと上手く書けます:

let mut x = 5u;

loop {
    x += x - 3;
    println!("{}", x);
    if x % 5 == 0 { break; }
}

これでloopによって永遠にループすることになったわけですが、途中で脱出するためにbreakを使うのです。

continueも同様ですが、これはループを終わらせるのではなく、次の回の反復に移行します。次の例は奇数だけを表示します:

for x in range(0i, 10i) {
    if x % 2 == 0 { continue; }

    println!("{:d}", x);
}

continuebreakの両方とも、forwhileのどちらの種類のループでも有効です。

11. 文字列

文字列は、どんなプログラマもがマスターすべき重要な概念です。Rustの文字列取り扱いのシステムは、Rustがシステムに焦点を当てているために、他の言語のものと少しばかり異なっています。可変サイズのデータ構造を扱っているときには、事態は複雑になりうるもので、文字列はまさにサイズ変更可能なデータ構造なのです。それはそれとして、また、Rustの文字列は、Cのような他のシステム言語とも違った動作をします。

では、詳細を掘り下げることにしましょう。 文字列 は、UTF-8のバイト・ストリームとしてエンコードされたユニコード数値のシークエンスです。更に付け加えると、文字列はナル終端ではないので、ナル文字を含むことができます。

Rustには&strStringという、2種類の主要な文字列があります。

第1のものは、&strで、これは「文字列スライス」と呼ばれます。文字列リテラルは、&str型です:

let string = "Hello there.";

この文字列は静的に割当されます。つまり、それはコンパイルされたプログラムの内部に保存され、プログラムの実行中全期間を通じて存在します。上のstringという束縛は、この静的に割当された文字列への参照です。文字列スライスは固定サイズを持ち、変更することができません。

他方で、Stringは、インメモリな文字列です。この文字列は伸長可能で、また、UTF-8であることが保証されています。

let mut s = "Hello".to_string();
println!("{}", s);

s.push_str(", world.");
println!("{}", s);

as_slice()メソッドによって、String&strに強制型変換をすることができます:

fn takes_slice(slice: &str) {
    println!("Got: {}", slice);
}

fn main() {
    let s = "Hello".to_string();
    takes_slice(s.as_slice());
}

Stringを定数文字列と比較するには、次のto_string()ではなく:

fn compare(string: String) {
    if string == "Hello".to_string() {
        println!("yes");
    }
}

as_slice()を用いてください:

fn compare(string: String) {
    if string.as_slice() == "Hello" {
        println!("yes");
    }
}

Stringから&strへの変換は安価ですが、逆に&strからStringへの変換にはメモリ割当が伴います。そうしなければならないのではない限り、そのような変換をする理由はまったくありません!
Rustの文字列の基本はこんなところです! もしスクリプト言語からやってきたならば、それらの言語で慣れ親しんできたよりは恐らく少しばかり複雑ではあるでしょうが、低水準の細部が重要になる場合には、それは本当に重要なのです。ここではただ、Stringがメモリ割当を伴いデータを制御するのに対して、&strは別の文字列への参照である、ということだけ覚えておいてください。そうすれば、それで準備万端です。

12. ベクター

他の多くのプログラミング言語同様に、Rustにも、なにかのもののリストが欲しい時のためのリスト型が備わっています。しかし、文字列の場合と同様に、リストという発想を表現する型が、Vec<T>(これは「ベクター」)、[T, .. N](これは「配列」)、&[T](これは「スライス」)と、様々あります。ヒュ~!
ベクターは文字列に似ていて、動的な長さを持ち、それにフィットするのに充分なメモリを割当します。ベクターはvec!マクロによって作成することができます:

let nums = vec![1i, 2i, 3i];

前に使ったことのあるprintln!()マクロと違って、vec!では角括弧([])を使っていることに注意してください。実はRustではどちらの場合にもどちらを使うこともできます。これは単なる規約的慣習に過ぎません。
配列は単に角括弧だけで作成することができます:

let nums = [1i, 2i, 3i];
let nums = [1i, ..20]; // これは全要素が1に初期化された要素数20の配列の略記法です

では、ベクターと配列の違いはなんでしょうか? 配列は固定サイズを持ち、要素を付け加えたり差し引いたりすることができません:

let mut nums = vec![1i, 2i, 3i];
nums.push(4i); // これは正しく動作します

let mut nums = [1i, 2i, 3i];
nums.push(4i); // error: type `[int, .. 3]` does not implement any method
               // in scope named `push`

push()メソッドによって、ベクターの末端に値を附加することができます。しかし、配列は固定サイズですから、要素を付け加えるということは意味をなしません。エラーメッセージからこの配列が持っている正確な型を知ることができます。[int, ..3]、つまり長さ3を伴ったintの配列です。
スライスは、&strと同様で、もうひとつ別の配列への参照です。as_slice()メソッドを使うことによってベクターからスライスを取得することができます:

let vec = vec![1i, 2i, 3i];
let slice = vec.as_slice();

これらの3つの型は全て、イテレータを返すiter()メソッドを実装しています。イテレータの詳細については後に話をしますが、いまのところは、このiter()メソッドによって、ベクター、配列、スライスの中身を表示するようなループが書けるということで充分です:

let vec = vec![1i, 2i, 3i];

for i in vec.iter() {
    println!("{}", i);
}

このコードは、各数字を順に表示します。
添字記法 を使うことで、ベクター、配列、スライスの特定要素にアクセスすることができます:

let names = ["Graydon", "Brian", "Niko"];

println!("The second name is: {}", names[1]);

他の殆どのプログラミング言語と同様に、これらの添字はゼロから始まります。ですから、最初の名前はnames[0]で、2番目の名前がnames[1]になります。上の例のコードは、The second name is Brianと表示します。
ベクターについては更にもっとたくさんのことがあるのですが、手始めにはこれで充分でしょう。さて、Rustの最も基本的な概念については全部を学習しました。これで、数当てゲームをビルドし始める準備ができたわけですが、まず最初に、知っておくべき最後のこと、つまり、キーボードからの入力の受け取り方、を知っておく必要があります。数当てをする能力抜きに数当てゲームはできませんから!

13. 標準入力

キーボードからの入力を受け取るのはとても簡単ですが、まだ目にしていない事項が必要になります。以下は入力を読んでそれを書き戻す単純なプログラムです:

fn main() {
    println!("Type something!");

    let input = std::io::stdin().read_line().ok().expect("Failed to read line");

    println!("{}", input);
}

さあ、この塊をひとつずつ見て行きましょう:

std::io::stdin();

これはstd::ioというモジュール内にあるstdin()という函数を呼び出しています。想像できると思いますが、std内にあるものは全てRustによって「標準ライブラリ」として提供されています。モジュールシステムについては後にもっとお話します。
完全な修飾名をいつでも書かされるのはイライラさせられるものですから、この函数をインポートする際にuse文を使うことができます:

use std::io::stdin;

stdin();

しかしながら、個々の函数をインポートするのではなくモジュールをインポートし、かつ1階層だけの修飾を用いることが、良いプラクティスだと考えられています:

use std::io;

io::stdin();

上の例を、このスタイルを使うようにして更新してみましょう:

use std::io;

fn main() {
    println!("Type something!");

    let input = io::stdin().read_line().ok().expect("Failed to read line");

    println!("{}", input);
}

次に来るのは:

.read_line()

です。read_line()メソッドは、stdin()の結果に対して呼び出すことができ、入力を1行まるごと返します。素晴らしく簡単ですね。

.ok().expect("Failed to read line");

次のコードを覚えていますか?

enum OptionalInt {
    Value(int),
    Missing,
}

fn main() {
    let x = Value(5);
    let y = Missing;

    match x {
        Value(n) => println!("x is {:d}", n),
        Missing => println!("x is missing!"),
    }

    match y {
        Value(n) => println!("y is {:d}", n),
        Missing => println!("y is missing!"),
    }
}

値があるかどうかを知るためには毎回マッチしなければいけませんでしたね。しかしながら、この場合にはxValueの値を持っていることはわかっています。それでも、matchMissingの場合も取り扱うよう我々に強制します。これは確かに99%の場合には望んでいる動作ですが、時には、我々のほうがコンパイラよりもよくわかっているということがあります。
同様にして、read_line()も入力を1行返してくるというわけではありません。read_lineは入力を1行返してくるかもしれないし、また返してこないかもしれないのです。これは、このプログラムがターミナルではなく、cronジョブだとかなにかほかの、標準入力がないような文脈で実行される場合に起こりえます。このため、read_lineは、既出のOptionalIntと大変によく似た、IoResult<T>という型を返してきます。IoResult<T>という型は、OptionalIntのジェネリックな形式なので、これについてはまだお話をしていません。その話をするときまでは、ただこれをintだけではなく他のどんな型にも使えるだけで、同じものだ、と考えておいてください。
RustはこれらのIoResult<T>に対するok()というメソッドを提供しています。これはmatch文と同じことをするのですが、有効な値があるものと想定してそれを行います。それから、その結果に対してexpect()を呼び出しますが、これは有効な値がないとそこでプログラムを終了させてしまいます。いまの場合、入力が得られないならばこのプログラムは動作しないのですから、そこのところは問題ありません。ですが、殆どの場合には、エラーの場合を明示的に処理したいはずです。このクラッシュが起きる場合にexpect()によってエラーメッセージを出すことができます。
これら全てがどのように動作しているのかについての正確な詳細はガイドの後の方で扱いますが、今のところは、これで作業するのに充分な基本的理解が得られるはずです。
作業中のコードに戻りましょう! コードを再掲します:

use std::io;

fn main() {
    println!("Type something!");

    let input = io::stdin().read_line().ok().expect("Failed to read line");

    println!("{}", input);
}

こういう長い行については、Rustは空白に関して幾らか融通を利かせてくれます。この例は次のように書くこともできるのです:

use std::io;

fn main() {
    println!("Type something!");

    let input = io::stdin()
                  .read_line()
                  .ok()
                  .expect("Failed to read line");

    println!("{}", input);
}

このような書き方をすることでコードが読みやすくなることもあります。読みにくくなることもあります。自分の判断力をきちんと使ってください。
これで標準入力から基本的な入力を得るのに必要な全てが揃いました! さほど込み入ってはいませんが、細かい部分がたくさんありましたね。

14. 数当てゲーム

OK! Rustの基本はマスターしました。もっと大きなプログラムを書きましょう。
最初のプロジェクトとして、古典的な入門者用プログラミング課題である、数当てゲームを実装してみましょう。その動作はこうなります。1から100の間のあるランダムな整数を生成します。それから、我々にその数についての推測値を入力するよう促します。推測値を入力すると直ちに、それが低すぎるか高すぎるかを示します。正しく推測できたなら、おめでとうをいい、そこまでに取られた推測値を表示します。よさげですよね?

14.1. セットアップ

新規のプロジェクトをセットアップしましょう。プロジェクト・ディレクトリに行ってください。hello_worldのときにどういうディレクトリ構造とCargo.tomlファイルを作成したか覚えていますか? Cargoにはそれを私達に代わってやってくれるコマンドが備わっています。やってみましょう:

$ cd ~/projects
$ cargo new guessing_game --bin
$ cd guessing_game

プロジェクトの名前をcargo newに渡し、それから--binフラグを渡します。これは、私達が作ろうとしているのはライブラリではなくバイナリファイルだからです。
生成されたCargo.htmlをチェックしてみましょう:

[package]

name = "guessing_game"
version = "0.0.1"
authors = ["Your Name <you@example.com>"]

Cargoはこの情報をあなたの環境から取得します。もしそれが正しくなければ、どうぞ修正してください。
最後に、Cargoはhello, worldプログラムを生成しています。src/main.rsをチェックしてみてください:

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

Cargoが作ってくれたものをコンパイルしてみましょう:

$ cargo build
    Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)

エクセレント! src/main.rsをもう一度開きましょう。コードは全部このファイルに書いていきます。複数ファイルを使うプロジェクトについてはこのガイドの後の方でお話します。
先に進む前に、Cargoのコマンドをもう一つお知らせしておきます。それはrunです。cargo runcargo buildと似たようなものですが、更に、生成された実行可能ファイルを実行します。試してみましょう:

$ cargo run
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
     Running `target/guessing_game`
Hello, world!

グレート! runコマンドはプロジェクトに対して素早く作業を反復するときに役にたちます。数当てゲームはまさにそういうプロジェクトで、次の作業に行く前に毎回素早くテストをする必要があるのです。

14.2. 推測を処理する

さあ始めましょう。数当てゲームのために最初にする必要があるのは、プレイヤーが推測値を入力できるようにすることです。以下のものをsrc/main.rsに書き込みましょう:

use std::io;

fn main() {
    println!("Guess the number!");

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");

    println!("You guessed: {}", input);
}

このコードについては、標準入力の話をしたときに既に見たことがあるはずです。std::ioモジュールをuseを使ってインポートし、それからmain函数にプログラムのロジックを置きます。ゲームのアナウンスをするちょっとしたメッセージを表示して、ユーザーに推測値を入力するよう求め、入力を取得して、それを表示します。
これについては標準入出力についての節で話をしましたから、ここでは詳細に立ち入りません。もし復習が必要なら、その節を再読してください。

14.3. 秘密の数を生成する

次に、秘密の数を生成する必要があります。そのためには、まだお話していないRustのランダム数値生成機能を使う必要があります。Rustは標準ライブラリに多数の興味深い函数を収録しています。もしちょっとしたコードが必要なら、それがあなたのために既に書かれている、ということがありうるのです! いまの場合には、Rustにランダム数値生成があることはわかっていますが、それをどう使うかがわかっていません。
ドキュメントへ行きましょう。Rustには特に標準ライブラリのドキュメント化のためのページがあります。そのページはここで見つけることができます。このページにはたくさんの情報がありますが、一番の優れものは検索バーです。ちょうど右上に検索語を入力できるボックスがあります。検索機能はいまのとこはまだかなり原始的ですが、日々改善されています。このボックスに'random'とタイプすると、ページがこれに切り替わります。検索結果のまさに一番最初がstd::rand::randomへのリンクです。それをクリックすれば、ドキュメントのページに行くことができます。
このページから幾つかのことがわかります。函数の型シグナチャ、いくばくかの説明文、そして使用例です。コードを修正して、random函数を追加しましょう:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random() % 100i) + 1i;

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");

    println!("You guessed: {}", input);
}

最初に変更したのは、ドキュメントの説明通り、use std::randを使うようにすることです。それから、let式を付け加えてsecret_numberという名前の変数束縛を作成し、その結果を表示します。
また、なんでrand::random()の結果に対して%を使っているのか、と思ったことでしょう。この演算子は「モジュロ」と呼ばれており、除算の際の剰余を返します。rand::random()の結果のモジュロを取ることによって、値が0から99の間に収まるように制約しているわkです。それから、この結果に1を加えて、1から100までの数にすます。モジュロを使うと、結果にごくごく僅かながら偏りが出るのですが、しかし、この例については大したことはありません。
これをcargo buildでコンパイルしてみましょう:

$ cargo build
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
src/main.rs:7:26: 7:34 error: the type of this value must be known in this context
src/main.rs:7 let secret_number = (rand::random() % 100i) + 1i;
                                   ^~~~~~~~
error: aborting due to previous error

うまくいきませんでした! Rustは「この文脈ではこの値の型が知られていなければなりません」と言っていますが、そりゃまたいったいどうしたんでしょう? ふむ、結論からいうと、rand::random()は、整数だけでなくごくたくさんの種類のランダム値を生成することができるのです。そして今回の場合、Rustにはrandom()がどの種類のランダム値を生成することになっているのかがわからないのです。ですから、Rustを助けてあげないといけません。数字リテラルであれば、末尾にiをつけるだけで、それが整数だということをRustに知らせることができます。しかし、これは函数についてはできません。函数については別の構文があり、このような感じになります:

rand::random::<int>();

これは「ランダムなint値をくださるようお願いします」と言っています。このヒントを使うようにコードを変更しましょう:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<int>() % 100i) + 1i;

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");

 println!("You guessed: {}", input);
}

この新しいプログラムを何回か実行してみてください:

$ cargo run
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
     Running `target/guessing_game`
Guess the number!
The secret number is: 7
Please input your guess.
4
You guessed: 4

$ ./target/guessing_game
Guess the number!
The secret number is: 83
Please input your guess.
5
You guessed: 5

$ ./target/guessing_game
Guess the number!
The secret number is: -29
Please input your guess.
42
You guessed: 42

ちょっと待った。-29ですと? 1から100の数が欲しいっていったのに! ここでは2つのやり方があります。random()に符号なし整数(これは正の数にしかなれません)を生成してくれるように頼むか、abs()函数を使って絶対値を得るか、です。ここでは符号なし整数の方でやってみましょう。ランダムな正数が欲しければ、ランダムな正数を要求したらよいのです。今度はコードはこんな感じになります:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");

    println!("You guessed: {}", input);
}

では、試して見ましょう:

$ cargo run
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
     Running `target/guessing_game`
Guess the number!
The secret number is: 57
Please input your guess.
3
You guessed: 3

グレート! お次は、秘密の値と推測値を比較してみましょう。

14.4. 推測値と比較する

このガイドの前の方で、2つの数を比較するcmp函数を作成したことを覚えていますか? それを、推測値を秘密の値と比べるmatch文と合わせて加えましょう:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");


    println!("You guessed: {}", input);

    match cmp(input, secret_number) {
        Less    => println!("Too small!"),
        Greater => println!("Too big!"),
        Equal   => println!("You win!"),
    }
}

fn cmp(a: int, b: int) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

コンパイルしようとすると、次のようなエラーになります:

$ cargo build
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
src/main.rs:20:15: 20:20 error: mismatched types: expected `int`
but found `collections::string::String` (expected int
but found struct collections::string::String)
src/main.rs:20 match cmp(input, secret_number) {
                         ^~~~~
src/main.rs:20:22: 20:35 error: mismatched types: expected `int` but found `uint` (expected int but found uint)
src/main.rs:20 match cmp(input, secret_number) {
                                ^~~~~~~~~~~~~
error: aborting due to 2 previous errors

これはRustプログラムを書いている時にはよくあることです。そして、これこそはRustの最も大きな力のひとつです。なにかのコードを試しに書いて、コンパイルできるかどうか見てみます。そうすると、Rustがなにか間違ってますよ、と教えてくれるのです。今回の場合、cmp函数は整数に対して動作するのですが、それに対して符号なし整数を与えてしまったのです。この場合、修正は容易です。だって、cmp函数は自分で書いたのですからね! 符号なし整数を取るように変更しましょう:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");


    println!("You guessed: {}", input);

    match cmp(input, secret_number) {
        Less    => println!("Too small!"),
        Greater => println!("Too big!"),
        Equal   => { println!("You win!"); },
    }
}

fn cmp(a: uint, b: uint) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

もう一度コンパイルしてみましょう:

$ cargo build
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
src/main.rs:20:15: 20:20 error: mismatched types: expected `uint`
but found `collections::string::String` (expected uint but found struct collections::string::String)
src/main.rs:20 match cmp(input, secret_number) {
                         ^~~~~
error: aborting due to previous error

このエラーは先程のものと似ています。uintを取得するつもりだったのに、代わりに文字列を取得してしまっていたのでした! これは、input変数が標準入力からやってくるからで、実はどんなものでも推測値にできてしまうのです。ためしてみましょう:

$ ./target/guessing_game
Guess the number!
The secret number is: 73
Please input your guess.
hello
You guessed: hello

おおっと! あと、コンパイルできなかったのにプログラムが実行できることに気が付くでしょう。これがうまくいくのは、コンパイルに成功した、以前のヴァージョンがまだその場所にあるからです。注意しなきゃいけませんね!
ともかく、取得できるのはStringです。でも、欲しいのはuintです、どうしたらいいんでしょう? ふむ、実はそのための函数があるのです:

let input = io::stdin().read_line()
                       .ok()
                       .expect("Failed to read line");
let input_num: Option<uint> = from_str(input.as_slice());

このfrom_str函数は、&str型の値を取り、それを何かに変換します。型のヒントを与えることによって、どんな種類のものかを教えてやります。random()のときの型のヒントを覚えてますか? こんな感じでしたね:

rand::random::<uint>();

ヒントを与えるのには別のやり方もあります。それは型をletの中で宣言するというものです:

let x: uint = rand::random();

この場合、xuintであるということを明示していますから、Rustはrandom()が何を生成すべきかを判別することができます。同様にして、以下のどちらのやり方もうまくいきます:

let input_num = from_str::<uint>("5");
let input_num: Option<uint> = from_str("5");

ともあれ、入力を数値に変換したわけなので、コードはこうなります:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");
    let input_num: Option<uint> = from_str(input.as_slice());



    println!("You guessed: {}", input_num);

    match cmp(input_num, secret_number) {
        Less    => println!("Too small!"),
        Greater => println!("Too big!"),
        Equal   => { println!("You win!"); },
    }
}

fn cmp(a: uint, b: uint) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

試してみましょう!

$ cargo build
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
src/main.rs:22:15: 22:24 error: mismatched types: expected `uint`
but found `core::option::Option<uint>` (expected uint but found enum core::option::Option)
src/main.rs:22 match cmp(input_num, secret_number) {
                         ^~~~~~~~~
error: aborting due to previous error

おお、そうでした! input_numuint型ではなくOption<uint>型なんでした。Optionを剥がなくてはいけません。もし前のことを覚えていれば、それにはmatchがピッタリなのでした。このコードを試してみましょう:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");
    let input_num: Option<uint> = from_str(input.as_slice());

    let num = match input_num {
        Some(num) => num,
        None      => {
            println!("Please input a number!");
            return;
        }
    };

    println!("You guessed: {}", num);

    match cmp(num, secret_number) {
        Less    => println!("Too small!"),
        Greater => println!("Too big!"),
        Equal   => { println!("You win!"); },
    }
}

fn cmp(a: uint, b: uint) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

matchを使って、Optionの内側からuintを取り出すか、或いは、エラーメッセージを表示して呼び出し元に戻ります。やってみましょう:

$ cargo run
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
     Running `target/guessing_game`
Guess the number!
The secret number is: 17
Please input your guess.
5
Please input a number!

えっ、なに? でもきちんと数を入力したじゃないの!
……実は、数を入力してはいないのです。stdin()から入力を一行取得するときには、入力の全部を取得することになります。つまり、エンターキーを押した時の\n文字も含めてなのですね。だから、from_str()"5\n"という文字列を見て、「うんにゃ、こりゃ数じゃねえやな。数じゃないやつがそこにはいっとるもんな!」というわけです。幸いなことに、ここで利用できる便利なメソッドtrim()&strには定義されています。このちょっとした修正でコードはこうなります:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    println!("Please input your guess.");

    let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");
    let input_num: Option<uint> = from_str(input.as_slice().trim());

    let num = match input_num {
        Some(num) => num,
        None      => {
            println!("Please input a number!");
            return;
        }
    };

    println!("You guessed: {}", num);

    match cmp(num, secret_number) {
        Less    => println!("Too small!"),
        Greater => println!("Too big!"),
        Equal   => { println!("You win!"); },
    }
}

fn cmp(a: uint, b: uint) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

試してみましょう!

$ cargo run
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
     Running `target/guessing_game`
Guess the number!
The secret number is: 58
Please input your guess.
  76
You guessed: 76
Too big!

やったね! 推測値の前にスペースを付け足してみたのですが、それでも76という推測をしたことを理解してくれています。このプログラムを何回か実行して、数の推測がきちんと動作していることを検証してください。数が小さすぎる場合も試してみてくださいね。
Rustコンパイラは、大いに我々を助けだしてくれました。このテクニックを「コンパイラにもたれかかる lean on the compiler」と言います。これはコードに取り組むさいにしばしば有用な方法です。正しい型に向かってエラーメッセージにヘルプ・ガイドをさせるのです。
さて、これでゲームの殆どは動作させられましたが、でも推測が一回しかできませんね。ループを付け加えてそこのところを変えましょう!

14.5. ループ処理

既にお話したように、loopキーワードを使えば無限ループができます。なので、それを付け足しましょう:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    loop {

        println!("Please input your guess.");

        let input = io::stdin().read_line()
                           .ok()
                           .expect("Failed to read line");
        let input_num: Option<uint> = from_str(input.as_slice().trim());

        let num = match input_num {
            Some(num) => num,
            None      => {
                println!("Please input a number!");
                return;
            }
        };


        println!("You guessed: {}", num);

        match cmp(num, secret_number) {
            Less    => println!("Too small!"),
            Greater => println!("Too big!"),
            Equal   => { println!("You win!"); },
        }
    }
}

fn cmp(a: uint, b: uint) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

さて、試してみましょう。でも待って、単に無限ループを付け加えただけなんじゃないの? うん。returnを付け加えたのを覚えてますか? もし数値でない答えを入れると、returnして、終了することになります。見ててください:

$ cargo run
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
     Running `target/guessing_game`
Guess the number!
The secret number is: 59
Please input your guess.
45
You guessed: 45
Too small!
Please input your guess.
60
You guessed: 60
Too big!
Please input your guess.
59
You guessed: 59
You win!
Please input your guess.
quit
Please input a number!

ははは! quitと入力したら本当に終了しましたよ。まあ、他のどんな非数値な入力でもそうなんですけどね。ふむ、これは控えめに言っても完全とはいえませんね。まず、ゲームに勝ったら実際にそこで終了するようにしましょう:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    loop {

        println!("Please input your guess.");

        let input = io::stdin().read_line()
                               .ok()
                               .expect("Failed to read line");
        let input_num: Option<uint> = from_str(input.as_slice().trim());

        let num = match input_num {
            Some(num) => num,
            None      => {
                println!("Please input a number!");
                return;
            }
        };


        println!("You guessed: {}", num);

        match cmp(num, secret_number) {
            Less    => println!("Too small!"),
            Greater => println!("Too big!"),
            Equal   => {
                println!("You win!");
                return;
            },
        }
    }
}

fn cmp(a: uint, b: uint) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

You win!の後にreturnを付け足せば、勝った時にプログラムを終了することになります。まだもうひとひねりする必要があります。つまり、もし非数値が入力されたときに、終了するのではなくて単にそれを無視したいのです。そこのところのreturncontinueに変更しましょう:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    println!("The secret number is: {}", secret_number);

    loop {

        println!("Please input your guess.");

        let input = io::stdin().read_line()
                               .ok()
                               .expect("Failed to read line");
         let input_num: Option<uint> = from_str(input.as_slice().trim());

        let num = match input_num {
            Some(num) => num,
            None      => {
                println!("Please input a number!");
                continue;
            }
        };


        println!("You guessed: {}", num);

        match cmp(num, secret_number) {
            Less    => println!("Too small!"),
            Greater => println!("Too big!"),
            Equal   => {
                println!("You win!");
                return;
            },
        }
    }
}

fn cmp(a: uint, b: uint) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

これでいいはずです! やってみましょう:

$ cargo run
   Compiling guessing_game v0.0.1 (file:///home/you/projects/guessing_game)
     Running `target/guessing_game`
Guess the number!
The secret number is: 61
Please input your guess.
10
You guessed: 10
Too small!
Please input your guess.
99
You guessed: 99
Too big!
Please input your guess.
foo
Please input a number!
Please input your guess.
61
You guessed: 61
You win!

マジすげえ! あと最後ほんのひとひねりで、数当てゲームは完成です。なんのことかわかりますか? そうです、その通り。秘密の数を表示したくはないですよね。テストするには結構でしたが、お蔭でゲームは台無しな感じです。というわけでこれが最終的なソースになります:

use std::io;
use std::rand;

fn main() {
    println!("Guess the number!");

    let secret_number = (rand::random::<uint>() % 100u) + 1u;

    loop {

        println!("Please input your guess.");

        let input = io::stdin().read_line()
                               .ok()
                               .expect("Failed to read line");
        let input_num: Option<uint> = from_str(input.as_slice().trim());

        let num = match input_num {
            Some(num) => num,
            None      => {
                println!("Please input a number!");
                continue;
            }
        };


        println!("You guessed: {}", num);

        match cmp(num, secret_number) {
            Less    => println!("Too small!"),
            Greater => println!("Too big!"),
            Equal   => {
                println!("You win!");
                return;
            },
        }
    }
}

fn cmp(a: uint, b: uint) -> Ordering {
    if a < b { Less }
    else if a > b { Greater }
    else { Equal }
}

14.6. 完成!

これで、みなさんは「数当てゲーム」を無事ビルドするのに成功しました! おめでとうございます!
いまやRustの基本的構文は学んでしまったことになります。これまでのところはどれも、みなさんが過去に使ったことのある様々なプログラミング言語と比較的似通っていたでしょう。これらの根本的な構文的・意味論的要素が残りのRust学習の基盤になります。
さて、これで基礎はマスターしたわけですから、Rustのもっとユニークな機能について学ぶ時が来ました。


原文の著作権ライセンスは下記のとおりです。
また、この日本語訳はApache License, version 2.0に拠ります。

Copyright © 2011-2014 The Rust Project Developers.
Licensed under the Apache License, Version 2.0 or the MIT license,
at your option.

This file may not be copied, modified, or distributed except
according to those terms.

65
62
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
65
62