はじめに - hello world!
C や C++ を使用しているとしたら、おそらくそれは、そうしなければならないからです。システムへの低レベルアクセスが必要であるか、最後の一滴まで性能を引き出す必要があるか、あるいはその両方が必要なのでしょう。Rust は、メモリに関する同じレベルの抽象化、同じ性能を提供しつつ、より安全で、より生産的にすることを目指しています。
具体的には、C++ よりも使いたいと思うかもしれない言語は世の中に数多くあります。Java、Scala、Haskell、Python などです。しかし、それらを使えないのは、抽象化レベルが高すぎる(メモリへ直接アクセスできない、ガベージコレクションの使用を強制される、など)か、性能上の問題がある(性能が予測不能であるか、単に十分に高速ではない)ためです。Rust はガベージコレクションの使用を強制せず、C++ と同様に、メモリへの生ポインターを扱うことができます。Rust は C++ の「使った分だけ支払う」という哲学を採用しています。ある機能を使わなければ、その機能が存在することによる性能上のオーバーヘッドを支払うことはありません。さらに、Rust のすべての言語機能には予測可能な(そして通常は小さな)コストがあります。
こうした制約により、Rust は C++ の(まれな)実行可能な代替手段になっていますが、Rust には利点もあります。Rust はメモリ安全です。Rust の型システムにより、C++ でよくある種類のメモリエラー、つまり未初期化メモリへのアクセスやダングリングポインターは、Rust ではすべて不可能になります。さらに、他の制約が許す限り、Rust は他の安全性の問題も防ごうとします。たとえば、すべての配列インデックスアクセスは境界チェックされます(もちろん、そのコストを避けたい場合は、安全性を犠牲にして避けることができます。Rust では、他の多くの unsafe なことと同様に、unsafe ブロック内でこれを行えます。重要なのは、Rust が unsafe ブロック内の unsafe 性を unsafe ブロック内に留め、それがプログラムの残りの部分に影響できないようにすることです)。最後に、Rust は現代的なプログラミング言語から多くの概念を取り入れ、それらをシステム言語の領域に導入しています。願わくは、それによって Rust でのプログラミングがより生産的で、効率的で、楽しいものになりますように。
このセクションの残りでは、Rust をダウンロードしてインストールし、最小限の Cargo プロジェクトを作成し、Hello World を実装します。
Rust の入手
Rust は http://www.rust-lang.org/tools/install から入手できます。 そこからのダウンロードには、Rust コンパイラー、標準ライブラリ、そして Rust のパッケージマネージャー兼ビルドツールである Cargo が含まれています。
Rust には stable、beta、nightly の 3 つのチャネルがあります。Rust は 6 週間ごとに新しいリリースを行う迅速なリリーススケジュールで運用されています。リリース日には、nightly が beta になり、beta が stable になります。
Nightly は毎晩更新され、最先端の機能を試したいユーザーや、自分のライブラリが将来の Rust でも動作することを確認したいユーザーに最適です。
Stable はほとんどのユーザーにとって正しい選択です。Rust の安定性保証は stable チャネルにのみ適用されます。
Beta は主に、コードが期待どおりに動作し続けることを確認するために、ユーザーの CI で使用されることを想定して設計されています。
したがって、おそらく stable チャネルを使うのがよいでしょう。Linux または OS X を使っている場合、それを入手する最も簡単な方法は次を実行することです。
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Windows では、同様に簡単な方法として次を実行できます。
choco install rust
その他のインストール方法については、http://www.rust-lang.org/tools/install を参照してください。
ソースは github.com/rust-lang/rust で見つけることができます。
コンパイラーをビルドするには、./configure && make rustc を実行します。より詳細な手順については、building-from-source を参照してください。
Hello World!
Rust プログラムをビルドする最も簡単で一般的な方法は、Cargo を使うことです。Cargo を使って hello というプロジェクトを開始するには、cargo new --bin hello を実行します。これにより、hello という新しいディレクトリが作成され、その中に Cargo.toml ファイルと、main.rs というファイルを含む src ディレクトリが作成されます。
Cargo.toml は、私たちのプロジェクトに関する依存関係やその他のメタデータを定義します。これについては後で詳しく戻ってきます。
すべてのソースコードは src ディレクトリに入ります。main.rs にはすでに Hello World プログラムが含まれています。次のようになっています。
fn main() { println!("Hello, world!"); }
プログラムをビルドするには、cargo build を実行します。ビルドして実行するには、cargo run を実行します。後者を行うと、コンソールに歓迎のメッセージが表示されるはずです。成功です!
Cargo は target ディレクトリを作成し、そこに実行可能ファイルを配置しています。
コンパイラーを直接使いたい場合は、rustc src/main.rs を実行できます。これにより、main という実行可能ファイルが作成されます。多くのオプションについては rustc --help を参照してください。
では、コードに戻りましょう。いくつか興味深い点があります。関数やメソッドを定義するには fn を使います。main() は私たちのプログラムのデフォルトのエントリーポイントです(プログラム引数については後に回します)。C++ のような別個の宣言やヘッダーファイルはありません。println! は Rust における printf 相当のものです。! は、それがマクロであることを意味します。標準ライブラリの一部は、明示的にインポート/インクルードしなくても利用できます(prelude)。println! マクロはそのサブセットの一部として含まれています。
例を少し変更してみましょう。
fn main() { let world = "world"; println!("Hello {}!", world); }
let は変数を導入するために使われます。world は変数名であり、これは文字列です(技術的には型は &'static str ですが、それについては後で詳しく説明します)。型を指定する必要はなく、推論されます。
println! 文で {} を使うのは、printf で %s を使うようなものです。実際には、それより少し一般的です。なぜなら、Rust は変数がすでに文字列でない場合でも、その変数を文字列に変換しようとするからです1(C++ の operator<<() のようなものです)。
この種のことは簡単に試して遊ぶことができます。複数の文字列を試したり、数値を使ったりしてみてください(整数リテラルと浮動小数点リテラルは動作します)。
必要であれば、world の型を明示的に与えることができます。
#![allow(unused)] fn main() { let world: &'static str = "world"; }
C++ では、型 T の変数 x を宣言するために T x と書きます。Rust では、let 文でも関数シグネチャなどでも、x: T と書きます。ほとんどの場合、let 文では明示的な型を省略しますが、関数引数では必須です。動作を確認するために、別の関数を追加してみましょう。
fn foo(_x: &'static str) -> &'static str { "world" } fn main() { println!("Hello {}!", foo("bar")); }
関数 foo には、文字列リテラルである単一の引数 _x があります(main から "bar" を渡しています)2。
関数の戻り値の型は -> の後に指定します。関数が何も返さない場合(C++ の void 関数)、戻り値の型をまったく指定する必要はありません(main のように)。非常に明示的にしたい場合は、-> () と書くことができます。() は Rust の void 型です。
Rust では return キーワードは必要ありません。関数本体(または他の任意のブロック、これについては後でさらに見ます)の最後の式がセミコロンで終わっていなければ、それが戻り値になります。したがって、foo は "world" を返します。return キーワードは依然として存在するため、早期 return を行うことができます。"world" を return "world"; に置き換えることができ、同じ効果になります。
なぜ?
上記の言語機能のいくつかについて、その動機を説明したいと思います。ローカル型推論は、安全性や性能を犠牲にすることなく便利で有用です(現在では現代的なバージョンの C++ にさえ含まれています)。小さな利便性として、言語項目が一貫してキーワード(fn、let など)で表されることが挙げられます。これにより、人の目でもツールでも走査しやすくなります。一般に、Rust の構文は C++ よりも単純で一貫性があります。println! マクロは printf より安全です。引数の数は文字列内の「穴」の数に対して静的にチェックされ、引数は型チェックされます。つまり、メモリを実際とは異なる型であるかのように出力したり、誤ってスタックのさらに先のメモリを参照したりするような printf の間違いを犯すことはできません。これらはかなり小さなことですが、Rust の設計の背後にある哲学を示していることを願っています。