Rust (プログラミング言語)

Rust（ラスト）は、性能、メモリ安全性、安全な並行性を目指して設計されたマルチパラダイムのプログラミング言語である。C言語、C++に代わるシステムプログラミング言語を目指しており、構文的にはC++に似ているが、「ボローチェッカー」(borrow checker) で参照の有効性を検証することによってメモリ安全性を保証できる。Rustはガベージコレクションなしでのメモリ安全性を達成しており、必要な場面で参照カウントを使うこともできる。

Rustプロジェクトはオープンソースのコミュニティベース開発で進行しており、言語仕様（検討段階含む）、ソースコード、ドキュメントはオープンソースライセンスで公開されている。2006年の開発初期は、Mozillaの従業員のグレイドン・ホアレ（Graydon Hoare）の個人プロジェクトだったが、2009年にMozillaが開発に関わり始めてMozilla Researchの公式プロジェクトとなった。2015年に1.0版がリリースされるまでにいくつもの破壊的な仕様変更があったが、1.0版以降は基本的には後方互換を保って6週間間隔で定期的にリリースされている。

Rustはマルチパラダイムプログラミング言語であり、手続き型プログラミング、オブジェクト指向プログラミング、関数型プログラミングなどの実装手法をサポートしている。基本的な制御構文はC言語に似ているが、その多くが式（expression）であるという点においてはML言語に似ている。コンパイル基盤にLLVMを用いており、実行時速度性能はC言語と同等程度である。強力な型システムとリソース管理の仕組みにより、メモリ安全性が保証されている。

Rustは2016–2022年の間Stack Overflow Developer Surveyで「最も愛されているプログラミング言語」で一位を獲得し続けている。一方で、Rustは学習難易度が高い言語とも考えられており、2017年ロードマップでは学習曲線の改善を目的として挙げていた。

Rustという名前はさび菌に因んで付けられた。この命名の後、当時の開発チームメンバーの多くが自転車を愛好していたことから、自転車のチェーンリングの錆つきを元にしたデザインのロゴが考案された。当然、チェーンリングの錆がさび菌によって生じるわけがなく、「設計者のグレイドン・ホアレが生物学オタクであることから酸化鉄の「錆」ではなく「さび菌」なのだ」と、参考文献の末尾に記されている。

 開発体制 
Rustはオープンソースのコミュニティベースで開発が進められている。プロジェクトの主管はRust Project Developers（Rust開発チーム）である。2018年現在、言語発明者のグレイドン・ホアレはプロジェクトチームから外れている。プロジェクトの全てのソースコードはGitHubで一般に公開されており、コミュニティメンバーの協力により改良が進められる。プロジェクトの大部分のコミットはコミュニティメンバーによるものである。

Mozillaの理念を守り、Rust言語は社会に開かれており、言語やツールに対する仕様の提案はRFCの形で取りまとめられ、関連するチケットで仕様詳細について利用者から感想や提言を受けている。

言語やコアライブラリを開発するRust開発チームはServo（ウェブブラウザ用レイアウトエンジン）やRustコンパイラの実装経験を通して言語を改良している。例えば、C言語とRustを連携させるコードを自動生成するbindgenは元々は外部開発者が開発していたが、Mozilla Servo開発チームがServoの開発で利用するためにC++とRustを連携させるコードも自動生成できるよう改良を重ね、現在はServo開発チームと密に連携しているRust開発チームに主管を移行してServoの開発と共に改良が続けられている。

 設計と特徴 

 パラダイム 
言語実装手法においてのプログラミングパラダイムは特定の実装手法に限定されない非純粋なマルチパラダイムプログラミング言語である。文法の表面的な記述は手続き型プログラミング、ブロックコードのまとまりの意味論は関数型プログラミング、型システムを用いたオブジェクト構造はオブジェクト指向プログラミングのパラダイム特性を持つ。特定のプログラミングパラダイムのみを使用してソースコードを記述することはなく、要所々々の必要な所で必要なパラダイムを使用してソースコードを記述することになる。

ソースコードをターゲットプラットフォームに最適化したオブジェクトファイルにコンパイルするコンパイル言語のパラダイム特性を持つ。多くのスクリプト言語が持つインタプリタ機能や、SwiftやGoが提供するようなREPL機能のパラダイム特性は持たない。

その他のパラダイムとして、関数の入出力パラメータにジェネリック型を指定するジェネリックプログラミング、非同期機能を実現する並行計算のパラダイム特性を持つ。

 基礎文法 
Rustの基礎的な文法はC言語やC++に似て、波括弧で囲まれたブロックコード{ ... }、if, else, whileなどの制御フローキーワードを持つ。
全てのC言語やC++のキーワードが実装されているわけではなく、一方で幾つかのRustの制御命令（例えばパターンマッチのためのmatch）はそれらの言語を習得したプログラマにとっては馴染みが少ないものも存在する。Rustの命令文は、表面的なC言語やC++との文法の類似性にもかかわらず、意味論的にはML系言語の命令式に近く、関数本体のほぼ全ての部分は制御フロー演算子でさえ「文（statement）」ではなく「式（expression）」である。例えば普通のif式もC言語で言う所の条件演算子であり、if式の結果として返り値を返す。

ブロックコード内の命令文のセパレータにはセミコロン（;）を用いるが、C言語のそれと異なりRustのセミコロンは直前の命令文がブロックコードで括られる式の途中式であることを宣言するためのものである。セミコロンを末尾に置かない命令文はブロックコードの最終的な評価式として扱われ、その式の結果がブロックコードの外へ戻り値として返される。これは関数定義であれば、結果を返す箇所にはセミコロンをつけない。結果が不要ならばセミコロンによって明示的に捨てるのである。そして結果を返さないならば、それは文と呼ばれる。

 Hello World 
「Hello, world!」を標準出力に出力するHello worldプログラム。
fn main() {
 println!("Hello, world!");
}

 階乗 
階乗を求めるプログラム。if式の結果として1もしくはn * fac_recursive(n-1)を返し、関数の結果としてif式の結果を返す。

fn fac_recursive(n: u32) -> u32 {
 if n <= 1 {
 1
 } else {
 n * fac_recursive(n-1)
 }
}

 変数 
変数はletキーワードで定義され、不変（イミュータブル）である。変数への再代入はコンパイルエラーとなる。

可変変数を扱いたい場合、変数宣言時にmutキーワードを利用して可変であることを明示しなければならない。この機能は例えば巨大構造体のコピーを避けながら一部を変更するために有効活用できる。

またRustはシャドーイング（同名変数の複数回定義）に対応している。シャドーイングにより一時的な変数の変更や変数型の変更が可能になる。

変数とは別にconstキーワードで定義される定数を持つ。定数は常に等しい値を持つ（mutやシャドーイングに対応していない）。

 型とポリモーフィズム 
この言語の型システムではHaskell言語に倣い「型クラス」を用いることができる。これはアドホックな多相性を容易にするものであり、可変型宣言により実現されるものである。高カインド多相性など、Haskell言語にある他の特徴はサポートされていない。

 型システム 
Rust言語が備える型システムは、impl（実装）、trait（トレイト）、struct（構造体）およびenum（列挙型）を基本として構成される。implが他の言語におけるクラスに近い役割を果たし、継承とポリモーフィズムはtraitによって提供される。traitにはメソッドを定義することができ、traitで宣言されたメソッドはimplによってstructへミックスインされる。structにはフィールドが定義可能で、traitとimplはそれ自身にはフィールドは定義できない。enumには複数種類の型のカテゴリ変数が定義可能で、数値型、文字列型、オブジェクト型などの複数の状態を選択的に持ちうる。菱形継承問題を回避するためにtraitのみが継承が可能である。

変数の型を決定する型システムは静的型付けかつ強い型付けである。静的型付けと動的型付けの区分においての型付けは、コンパイル時に全ての変数に対して型を決定する静的型付けを基本としているが、トレイトをポインタを介して利用することで、ダックタイピングに似た型付けが可能なトレイトオブジェクトが存在する。ほかに、Anyトレイトにて、実行時リフレクションを用いた動的型付けも可能である。強い型付けと弱い型付けの区分においての型付けは、実行時に変数の型を型変換（アップキャスト、ダウンキャスト、クロスキャスト）およびボクシングをすることを許さない強い型付けのみをサポートしている。C言語、Javaは実行時に型変換をすることを許す弱い型付けをサポートしているが、Rust言語では変換先の型が明示されていない状況での型変換は認められていない。なお、型が確定している変数への代入や、シグネチャによりやはり型が確定している関数への引数渡しおよび返値の受け取り等に際しては、型強制のための関数が定義されていればそれを暗黙のうちに呼び出すことができる。

 型推論 
Rustコンパイラは変数への代入時（variable = value）、変数の型を値の型に基づき型推論する。変数の宣言には必ずしも型を決定するための初期値を必要としない。変数の宣言時に初期値が与えられた場合は「変数の型」は「初期値の型」であると型推論がなされるが、初期値が与えられなかった場合は以降のブロックコード中のその変数へ値が初めて代入された時に「左辺の変数の型」は「右辺の代入する値の型」であると型推論がなされる。変数への代入が型不一致により失敗した場合にはコンパイル時にエラーを検出する。

 ポリモーフィズムの実現 
ポリモーフィズムを実現するため、構造体のフィールドおよび関数の入出力値は特定のトレイトの実装をするジェネリック型を指定することが出来る。そのような定義の中では、ジェネリック型で型が宣言された変数および入出力値はそのトレイトの特性のみ使用できる。これはジェネリック関数が定義されるとすぐに型判定ができることを意味している。これはC++のダック・タイピングで具体的な型がインスタンス化されるまで判定できないテンプレートとは対照的である。しかし、Rustのジェネリクスの実装はC++のテンプレートの典型的な実装と似ており、インスタンス化ごとにコードの個別のコピーが生成される。これは単相化と呼ばれ、JavaやHaskellで通常使用される型消去方式とは対照的である。単相化の利点は特定のユースケースごとに最適化されたコードであり、欠点は結果のバイナリのコンパイル時間およびサイズが増加することである。Rustのトレイトを用いたポリモーフィズムの実現は、実行時のオーバーヘッドがない「ゼロコスト抽象化」と表現されている。

 リソース管理 
システムプログラミング言語には効率良いリソース管理機能が必須である（例: メモリ・ファイル管理）。C言語はプログラマがリソースを直接管理し高い効率を得られる反面、メモリリークのようなバグ混入の危険性を孕んでいる。より高級な言語（例: Java）ではガベージコレクションのような動的リソース管理機構により高い安全性を得られる反面、動的管理のオーバーヘッドが必ず付きまとう。

Rustは所有権を軸としRAII・参照・借用チェッカーなどを言語仕様として組み入れることで、リソース管理検証をコンパイル時・静的におこなう。ゆえに動的管理のオーバーヘッドを避けつつ、安全性が担保されたリソースアクセスが可能になっている（c.f. ゼロコスト抽象化）。

以下はRustのリソース管理を支える個別の概念である。

 所有権 
所有権（）はリソース管理の中心を成す重要な概念である。一つのリソースは一つの所有者（変数やブロック）のみに関連付けされるという制約がRust特有のリソース管理機能として存在する。これは、あるリソースの所有権はある所有者（owner）が持っている、のように表現される。同時に、リソースの所有権を複数の変数が持つことは出来ない。

これはC++のスマートポインタの一種であるunique_ptrの振る舞いに似ている。