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~2025年の間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の開発と共に改良が続けられている。

LLMの使用制限 2026年8月5日、Rustプロジェクトの主要チームは、メインリポジトリ(rust-lang/rust)における大規模言語モデル(LLM)の利用ポリシー(LLM Usage Policy)を発表した。このポリシーでは「質問・分析・推敲・レビューへの使用は許可するが、生成(作成)目的の使用は厳しく制限する」という原則が掲げられた。

具体的には、コードベースの解析やプライベートな自己レビューへの利用、自力での実装に向けた学習用途などは許可される一方、LLMによるドキュメントやコンパイラ診断メッセージの作成、個人アカウントからのLLM生成文章の直接投稿などは禁止されている。LLMによるソースコードの作成・提案については原則として制限されるが、タイポ修正などの軽微な変更や、事前にレビュアーの同意を得た非重要な領域における実験的貢献(開示とテストの網羅が必須)などに限り条件付きで認められている。なお、発言の機械翻訳にLLM等を使用する場合はその旨の開示が義務付けられており、誤解を防ぐため原文(母国語)もあわせて投稿することが推奨されている。

設計と特徴

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

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

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

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

ブロック内のセパレータ・ターミネータにはセミコロン(;)を用いる。Pascalのそれが単なる文と文を区切るものであるのとは異なりC言語のセミコロンはセパレータではなく(for文に含まれるセミコロンを除く)、文に含まれているものであり、いくつかの種類の文の最後に文の一部分として付いているもの、である。、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キーワードで定義され、不変(イミュータブル)である"by default variables are immutable." The Rust Programming Language。変数への再代入はコンパイルエラーとなる"E0384. An immutable variable was reassigned." Rust Compiler Error Index。

可変変数を扱いたい場合、変数宣言時にmutキーワードを利用して可変であることを明示しなければならない"you can make them mutable by adding mut in front of the variable name." The Rust Programming Language。この機能は例えば巨大構造体のコピーを避けながら一部を変更するために有効活用できる。

また、Rustではシャドーイング(同名変数の複数回定義)を明示的に利用できる"you can declare a new variable with the same name as a previous variable, and the new variable shadows the previous variable." The Rust Programming Language。シャドーイングを行うと後から定義した変数が同名の前の変数へのアクセスを阻害するため、一時的な変数の変更や、変数型の変更など元の変数にアクセスさせたくない場合によく用いられる"By using let, we can perform a few transformations on a value but have the variable be immutable after those transformations have been completed. ... The other difference between mut and shadowing is that because we’re effectively creating a new variable when we use the let keyword again, we can change the type of the value but reuse the same name." The Rust Programming Language。

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

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

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

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

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

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

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

参照 参照()は「他者所有値の一時的な借り受け」を表現する値である"A reference represents a borrow of some owned value." Primitive Type reference. The Rust Standard Library. 2022-12-24閲覧.。すなわち参照とは、値の所有権を元の所有者に置いたまま作成される、値へのアクセス扉である"A reference ... to access the data stored at that address; that data is owned by some other variable." References and Borrowing. The Rust Programming Language. 2022-12-24閲覧.。参照を介して参照先の値を読み書きできる一方、参照を解放しても(所有権がないので)参照先の値は存在し続ける。参照は常に有効な参照先を指すと保証されている"A reference cannot outlive its referent" References. The Rustonomicon. 2022-12-24閲覧."A lifetime is said to “outlive” another one if its representative scope is as long or longer than the other." Primitive Type reference. The Rust Standard Library. 2022-12-24閲覧.。参照を得ることを借用()という。

参照はいくつかの問題を解決するためにRustへ導入された。まず値へのアクセスを所有者のみに限定した場合、所有者変数がブロック/関数の内外を引き回されて冗長になる"The issue ... we have to return the String to the calling function so we can still use ... . Instead, we can provide a reference" References and Borrowing. The Rust Programming Language. 2022-12-24閲覧.。値の所有権を有さずにアクセスを可能にする、扉・プロキシ・エイリアスのようなオブジェクトがあればこれは解決する。しかし単にアクセスオブジェクトを渡すとその有効範囲が不明なためダングリング参照を起こしうる。これは参照と参照先の生存期間等を比較することで静的に検出できる(借用チェッカー)。これにより参照は常に有効になり、ヌル参照チェックも不要になる。このような背景で導入されたのがRustの参照である。

参照に関する型・演算子・式が存在する。借用を表す式を借用式 &expr()という"Syntax BorrowExpression" Operator expressions. The Rust Reference. 2022-12-24閲覧.。型・演算子は以下である。 +表. 参照に関する型と演算子参照型()"Primitive Type reference ... References, &T and &mut T." Primitive Type reference. The Rust Standard Library. 2022-12-24閲覧.借用演算子()"Borrow operators" Operator expressions. The Rust Reference. 2022-12-24閲覧.共有参照型 &T()"shared reference type is written &type" Pointer types. The Rust Reference. 2022-12-24閲覧.共有借用演算子 &()"The & (shared borrow) ... operators" Operator expressions. The Rust Reference. 2022-12-24閲覧.可変参照型 &mut T()"A mutable reference type is written &mut type" Pointer types. The Rust Reference. 2022-12-24閲覧.可変借用演算子 &mut()"&mut (mutable borrow) operators" Operator expressions. The Rust Reference. 2022-12-24閲覧. すなわち、演算子 & を用いた式 &expr の評価により型 &T の値が得られる。&mut でも同様である。

共有参照型は参照先の読み取りのみが可能で書き換えができないが、値が不変であるため同じ値を指す複数の共有参照が生成できる"When a shared reference to a value is created, it prevents direct mutation of the value." Pointer types. The Rust Reference. 2022-12-24閲覧.。参照はCopy トレイトによりムーブでなくコピーで渡される"The following traits are implemented for all &T, regardless of the type of its referent: Copy" Primitive Type reference. The Rust Standard Library. 2022-12-24閲覧.。可変参照型は両方が可能であるが、値が可変であるため同じ値を指す他の参照が生成できない"if you have a mutable reference to a value, you can have no other references to that value." References and Borrowing. The Rust Programming Language. 2022-12-24閲覧.。ゆえに参照は Copy トレイトを持たずコピーでなくムーブで渡される"&mut T references get all of the above except Copy and Clone (to prevent creating multiple simultaneous mutable borrows)" Primitive Type reference. The Rust Standard Library. 2022-12-24閲覧.。

Rawポインタ Rawポインタ(、生ポインタ)は、メモリ上の特定のアドレスを直接指す型である。参照(&Tや&mut T)と似ているが、ボローチェッカーによる所有権や借用規則の監視対象とならない。

Rawポインタの生成自体はsafe Rustで行えるが、ポインタを解参照して値を読み書きするにはunsafeブロックが必要となる。

主に、Rust以外の言語で実装された外部ライブラリとの外部関数インターフェース(Foreign Function Interface、FFI)によるアドレス受け渡しや、unsafeと組み合わせてボローチェッカーの制約を超えたデータ構造やメモリ操作を行う際に用いられる。

ボローチェッカー 既に解放されたリソースを指し続ける参照はバグを引き起こす(例: ダングリングポインタ)。もし静的にこれを検証できれば、オーバーヘッド無しに安全性を担保できる。また検証により不正な参照を避けられるため参照へのヌル代入が不要となり、ヌルポインタチェックを不要にできる。

Rustではボローチェッカー(、借用チェッカー)により所有権の競合および不正利用を静的に検証する。所有権が解放されたリソースを指す参照は無効であり、使用した場合はコンパイルエラーとなる。ボローチェッカーは参照についてもライフタイム(lifetimes)としてリソースの生存期間を検証する。これによりヌルポインタやダングリングポインタ、リソース利用競合を制限したメモリ安全性を実現している。

特異なリソース型 リソースのメモリ確保は基本的にはスタックメモリを利用しており、ヒープメモリを利用したメモリ確保はBoxやVecなどの特定の型のみで限定的に利用されている。

コンパイル時に型のサイズが決定できない可変長配列を表すような型はヒープメモリを利用するBox型を使用してリソースを管理する。Boxリソース自体はスタックメモリで管理されるが、Boxリソースが所有権を持つ実体のリソースはヒープメモリで管理される。標準のヒープメモリ確保にはシステムアロケータを利用するが、対象プラットフォームやビルド時の指定により他のメモリ確保APIを利用することもある。ヒープメモリはスタックメモリに比べて速度性能が落ちるため、必要時にのみ限定的に利用されている。

境界チェック Rustは実行時に境界チェックをおこなう。これによりバッファオーバーランをはじめとしたメモリアクセスに対する安全性を得ている。この機能はゼロコスト抽象化でなく実行時の命令であるため、安全性と引き換えにいくらかの性能オーバーヘッドが発生している。他のシステムプログラミング言語として代表的なC言語やC++は境界チェックを強制しないため、Rustの特徴の1つとなっている。

ライフタイム注釈 ライフタイム注釈()はジェネリック型の一種で、リソースやそれらへの参照の生存期間(ライフタイム、)を表す。

参照を関数やメソッドとの間で受け渡ししたり、構造体や列挙型など別のデータ型に保存すると、参照の生存期間が参照を定義したスコープだけでは決まらなくなる。この場合、参照や参照先リソースに対してライフタイム注釈の付加が必要となる。ライフタイム注釈は'lifetimeの形式で記述する。ライフタイム注釈を参照型に付加する場合は&の直後、それ以外の型に対してはジェネリック型として付加する。この他、別のジェネリック型に対してライフタイム注釈を付加することもできる。特に、あるライフタイム注釈に対して別のライフタイム注釈を境界条件として与え、どちらか片方がもう片方と同等かそれより短い生存期間を持つことをコンパイラに明示することができる。

ボローチェッカーはライフタイム注釈を持つ変数に対し、その変数が指す参照先やリソースの生存期間がライフタイム注釈により要求される生存期間を満たしているかどうかをチェックする。チェックに失敗した場合はコンパイルエラーとする。注意すべき点として、ライフタイム注釈はライフタイムをコンパイラに明示する記法に過ぎないため、ライフタイム注釈を与えることにより参照先やリソースの生存期間が変更されることはなく、延長することもできない。それらの生存期間はあくまでそれらを定義および使用するスコープによって決まるため、ライフタイム注釈のチェックでエラーが出た場合は参照先の生存期間を修正する必要がある。

関数やメソッドの定義にあっては、付加すべきライフタイム注釈に、ある特定のパターンが存在することが知られている。そのようなパターンの通りにライフタイム注釈を指定したい場合は、ライフタイム注釈の手動付加を省略し、コンパイラにライフタイム注釈を自動付加させることができる。この機能はライフタイムエリゾン()と呼ばれる。ライフタイムエリゾンのパターン以外のライフタイム注釈が必要な場合はライフタイムエリゾンは使用できず、ライフタイム注釈を明示的に付加しなければならない。