← コース一覧
Rust
所有権モデルによってメモリ安全性をコンパイル時に保証するシステムプログラミング言語です。変数や関数の基礎から、所有権・借用、コレクション、エラー処理、トレイト、イテレータまでを1テーマずつ順番に学びます。
公式ドキュメント: The Rust Programming Language ↗基本文法
- 1Cargo — プロジェクトとクレートの管理Cargo は Rust に標準で付いてくる、プロジェクトの作成・ビルド・実行・テストをまとめて担うツールです。cargo new プロジェクト名 で雛形を作ると、ソースを置く src フォルダと、設定ファイル Cargo.toml が用意されます。Rust の開発はほぼ必ず Cargo を通して行うので、最初に押さえておきたい土台です。
- 2実行可変数と可変性 — let と mutRust では let で変数を宣言します。宣言した変数はデフォルトで不変(イミュータブル)で、あとから値を代入し直すことはできません。書き換えたい変数には let mut のように mut キーワードを付けます。
- 3実行可基本型と型推論Rust は静的型付け言語で、整数の i32 や u64、浮動小数点数の f64、真偽値の bool、文字の char などの基本型があります。型は let x: i32 = 5; のようにコロンの後ろに書きますが、多くの場合は右辺から推論されるので省略できます。
- 4実行可シャドーイング — 同じ名前で再宣言let で宣言済みの名前をもう一度 let で宣言し直すことを「シャドーイング」と呼びます。再代入と違って mut は不要で、新しい変数が古い変数を覆い隠す形になるため、型を変えることもできます。
- 5実行可関数 — 文と式の違い関数は fn で定義し、引数には型注釈が必須で、戻り値の型は -> の後ろに書きます。Rust では「文(セミコロンで終わり値を返さない)」と「式(値を返す)」が区別され、関数本体の最後にセミコロンなしで式を置くと、それが戻り値になります。
- 6実行可if 式 — 条件分岐条件分岐は if 条件 { ... } else { ... } と書き、条件に丸括弧は不要です。条件は必ず bool でなければならず、数値を「0 でなければ真」のように扱うことはできません。他言語の癖でつまずきやすいポイントです。
- 7実行可loop と while — 条件による繰り返しloop { ... } は無限に繰り返すループで、break で抜けます。break に値を添えると、その値が loop 式全体の値になります。条件を毎回チェックしながら繰り返すには while 条件 { ... } を使います。
- 8実行可for とレンジ — 回数が決まった繰り返しfor は for i in 0..3 { ... } のように、レンジ(範囲)を順に回るループです。0..3 は 0, 1, 2 を表し、終端を含めたいときは 1..=3 と書きます。
所有権
- 9実行可所有権 — 値はムーブするRust では、すべての値にただ一人の「所有者」となる変数がいます。String のようなヒープ上のデータを別の変数に代入すると、所有権が移動(ムーブ)し、元の変数はその時点で使えなくなります。
- 10実行可Copy と Clone — ムーブしない型・明示的な複製i32 や bool、char などの単純な型は Copy という性質を持ち、代入すると値がそのまま複製されます。ムーブが起きないので、代入後も元の変数を使い続けられます。
- 11実行可参照と借用 — &T値の所有権を渡さずに中身を読みたいときは、& を付けて参照を作ります。参照を受け取ることを「借用」と呼び、関数の引数を &String のように宣言すれば、呼び出し後も元の変数をそのまま使えます。
- 12実行可可変参照 — &mut T借用した値を書き換えたいときは、&mut で可変参照を作ります。参照される側の変数も let mut で宣言されている必要があり、関数側は引数を &mut String のように受け取ります。
- 13実行可スライス — データの一部分への参照スライスは、String や配列の「一部分」を指す参照です。&s[0..5] のようにレンジで範囲を指定して作り、データを複製せずに部分列を扱えます。文字列のスライスは &str という型になります。
構造体
- 14実行可struct の定義 — 関連データをまとめるstruct は複数の値に名前を付けてひとまとめにする型です。struct User { name: String, age: u32 } のようにフィールドを定義し、User { name: ..., age: ... } という形でインスタンスを作り、ドット記法でフィールドにアクセスします。
- 15実行可メソッド — impl ブロックと &self構造体に振る舞いを持たせるには、impl 構造体名 { ... } というブロックの中に関数を書きます。第1引数を &self にすると、rect.area() のようにドット記法で呼べるメソッドになります。
- 16実行可関連関数 — ::new とコンストラクタimpl ブロックには self を取らない関数も書けます。これを関連関数と呼び、Circle::new(3.5) のように「型名::関数名」で呼び出します。String::from もこの形です。
列挙型とパターン
- 17実行可列挙型 enumenum は「取りうる値の種類を列挙する」型です。enum Status { Loading, Success, Error } のように定義し、Status::Loading の形で値を作ります。変数に入るのは、列挙したバリアントのどれか1つだけです。
- 18実行可パターンマッチ matchmatch は値をパターンと照合して分岐する式です。match value { パターン => 処理, ... } の形で書き、enum のバリアントごとの分岐や、バリアントが持つデータの取り出しが1つの構文でできます。
- 19実行可if let — 1パターンだけの照合「特定のバリアントのときだけ処理したい」場面では、match の代わりに if let パターン = 値 { ... } と書けます。パターンに一致したときだけ中の処理が実行され、バリアントが持つデータも取り出せます。
モジュールとパッケージ
- 20実行可mod — コードを名前空間で分けるプログラムが大きくなったら、関連する関数を mod モジュール名 { ... } というブロックでひとまとまりにできます。モジュールの中の関数は、モジュール名::関数名 というパスで呼び出します。なお、モジュールの中身はそのままでは外から見えないため、例では関数に pub を付けています(pub の詳細は次のレッスンで学びます)。
- 21実行可pub — 公開範囲を制御するモジュールの中の関数や構造体は、デフォルトではすべて非公開で、モジュールの外から呼び出せません。外に見せたいものにだけ pub を付ける、というのが Rust の可視性のルールです。
- 22実行可use — パスを短くするモジュールの階層が深くなると、毎回フルパスで書くのは冗長です。ファイルの上部に use パス; と書いておくと、そのパスの最後の要素を短い名前のまま使えるようになります。
コレクション
- 23実行可Vec<T> — 可変長の配列Vec<T> は要素を増減できる可変長の配列です。vec![10, 20, 30] マクロで初期値つきで作り、push で末尾に追加、numbers[0] のように添字でアクセスします。
- 24実行可String — 伸長できる文字列String はヒープ上に文字列を持ち、あとから伸ばせる型です。String::from("...") やリテラルの .to_string() で作り、push_str で連結します。複数の値を組み立てるなら format! マクロが便利です。
- 25実行可HashMap<K, V> — キーと値のペアHashMap<K, V> はキーから値を引ける表です。標準ライブラリの std::collections にあるため、ファイル先頭で use std::collections::HashMap; と書いてから使います。insert で登録し、map["key"] のようにキーで取り出せます。
エラー処理
- 26実行可panic! — 回復不能なエラーpanic! マクロを呼ぶと、メッセージを表示してプログラムがその場で停止します。配列の範囲外アクセスなどでも内部的に panic が起きており、「これ以上続行できない」状態を表す仕組みです。
- 27実行可Option<T> — null の代わりRust には null がなく、「値があるかもしれないし、ないかもしれない」は標準の enum である Option<T> で表します。値があるなら Some(値)、ないなら None で、中身を使うには match などで取り出します。
- 28実行可Result<T, E> — 回復可能なエラー失敗しうる処理の結果は、標準の enum である Result<T, E> で表します。成功なら Ok(値)、失敗なら Err(エラー) を返し、呼び出し側は match で両方のケースを処理します。
- 29実行可? 演算子 — エラーの委譲Result を返す式の後ろに ? を付けると、「Ok なら中身を取り出して続行、Err ならその場で関数から Err を return」という処理を1文字で書けます。エラーを呼び出し元に委譲するときの定番構文です。
- 30実行可From と Into — 型の変換とエラーの自動変換From は「ある型から別の型を作る」変換を定義するトレイトです。impl From<A> for B と書いて from 関数を実装すると、B::from(a) で A から B を作れます。From を実装すると、対になる Into(a.into() で B を得る)も自動で使えるようになります。型と型の橋渡しを標準化する仕組みです。
ジェネリクスとトレイト
- 31実行可ジェネリック関数 — 型をパラメータにするfn swap<T, U>(...) のように関数名の後ろに <T> を書くと、T を「あとで決まる型」として使えます。これをジェネリクスと呼び、i32 用・f64 用と同じ関数を型ごとに書き分ける重複をなくせます。
- 32実行可トレイトの定義と実装トレイトは「この型はこういうメソッドを持つ」という共通の振る舞いの約束です。trait Greet { fn greet(&self) -> String; } のようにシグネチャだけを定義し、impl Greet for Dog { ... } の形で型ごとに実装します。
- 33実行可トレイト境界 — ジェネリクスに条件を付けるジェネリック関数の中で比較や表示などの操作をするには、<T: PartialOrd> のように「T はこのトレイトを実装していること」という条件を付けます。これをトレイト境界と呼びます。
ライフタイム
- 34実行可ライフタイムとは — 参照の有効期間参照には「いつまで有効か」という期間があり、これをライフタイムと呼びます。参照の指す先の値がスコープを抜けて消えたのに参照だけが残ると、消えたデータを読む危険な状態(ダングリング参照)になるため、Rust はこれをコンパイル時に検出して拒否します。
- 35実行可ライフタイム注釈 — <'a>複数の参照を受け取って参照を返す関数では、「返す参照がどの引数と同じ期間生きるのか」をコンパイラが判断できないことがあります。そこで fn longest<'a>(a: &'a str, b: &'a str) -> &'a str のように <'a> という注釈を書き、引数と戻り値のライフタイムの関係を伝えます。
- 36実行可構造体に参照を持たせる構造体のフィールドに参照を持たせるには、struct Excerpt<'a> { part: &'a str } のように構造体自体にライフタイムパラメータを付けます。これは「Excerpt のインスタンスは、part が指す元データより長生きできない」という宣言です。
テスト
クロージャとイテレータ
- 39実行可クロージャ — その場で作る無名関数クロージャは |引数| 式 という形でその場に書ける無名関数です。let add = |a, b| a + b; のように変数に入れて、普通の関数と同じように呼び出せます。引数や戻り値の型は多くの場合推論されます。
- 40実行可イテレータ — 要素を順に取り出すイテレータは「要素を順に取り出せるもの」を表す仕組みです。Vec なら .iter() でイテレータを作り、.next() を呼ぶたびに Some(次の要素) が返り、要素が尽きると None が返ります。
- 41実行可map・filter・collect — 変換パイプラインイテレータにはクロージャを受け取るメソッドが用意されています。map は各要素を変換し、filter は条件を満たす要素だけを残し、collect で結果を Vec などのコレクションに集めます。
スマートポインタ
- 42実行可Box<T> — ヒープにデータを置くBox<T> は「値をヒープに置き、その場所を指すポインタ」を持つ型です。Box::new(5) で値をヒープに移し、変数自体にはポインタだけが残ります。中身には * で届き、メソッドはドット記法でそのまま呼べます。
- 43実行可Deref — スマートポインタを参照のように使うBox の中身に * で届けるのは、Box が Deref トレイトを実装しているからです。Deref は「* を付けたらどの値への参照を返すか」を決めるトレイトで、impl<T> Deref for MyBox<T> のように自作の型にも実装できます。
- 44実行可Drop — 値が消えるときの処理Drop トレイトを実装すると、値がスコープを抜けて破棄される瞬間に呼ばれる処理を書けます。fn drop(&mut self) に後片付けのコードを書いておけば、所有者が消えるタイミングでコンパイラが自動で呼び出してくれます。
- 45実行可Rc<T> — 所有者を複数にする所有権のルールでは値の所有者は常に1人ですが、Rc<T>(参照カウント)で包むと複数の所有者で1つの値を共有できます。Rc::clone(&data) は中身を複製せず「所有者の数」を1増やすだけで、カウントが0になった時点で値が解放されます。
- 46実行可RefCell<T> — 実行時に借用を検査するRefCell<T> は借用ルールの検査をコンパイル時ではなく実行時に行う型です。borrow() で読み取りの借用、borrow_mut() で書き換えの借用を取り出せて、let mut なしの変数の中身でも書き換えられます。これを「内部可変性」と呼びます。
並行処理
- 47実行可thread::spawn — スレッドを起動するthread::spawn にクロージャを渡すと、その処理を実行する新しいスレッドが起動します。spawn は JoinHandle を返し、handle.join() を呼ぶとそのスレッドが終わるまで待てます。クロージャの最後の値がスレッドの返り値になり、join().unwrap() で受け取れます。
- 48実行可move クロージャ — スレッドに所有権を渡すスレッドのクロージャで外の変数を使うときは、クロージャの前に move を付けて所有権ごとスレッドに渡します。スレッドが元の変数より長く生きる可能性があるため、借用のままではコンパイラが安全を保証できず、move なしではコンパイルエラーになります。
- 49実行可チャネル — スレッド間でデータを送るmpsc::channel() は送信機 tx と受信機 rx のペアを作り、tx.send(値) で送ったデータを別スレッドの rx で受け取れます。値は所有権ごと送られるので、送信側はその後その値に触れません。「メモリを共有して通信するのではなく、通信してデータを共有する」というのがチャネルの考え方です。
- 50実行可Mutex と Arc — 共有状態を安全に更新する複数のスレッドで同じ値を書き換えたいときは Mutex<T> で包みます。lock().unwrap() でロックを取ると中身への書き換え可能なアクセスが得られ、その値がスコープを抜けると自動でロックが解放されます。ロックを持てるのは同時に1スレッドだけなので、更新が混ざって壊れることがありません。
トレイトオブジェクト
- 51実行可dyn Trait — 異なる型を同じ入れ物にBox<dyn Trait> と書くと「このトレイトを実装した何かの型」を指す「トレイトオブジェクト」になります。Vec<Box<dyn Shape>> のようにすれば、Circle と Square のような異なる型をひとつのベクタに入れて、共通のメソッドを同じ書き方で呼び出せます。
- 52実行可静的ディスパッチと動的ディスパッチ — ジェネリクスとの使い分けジェネリクス(<T: Greet>)は、使われた型ごとに専用の関数をコンパイル時に作る「静的ディスパッチ」です。一方 &dyn Greet は、どの実装を呼ぶかを実行時に対応表(vtable)から探す「動的ディスパッチ」です。書き方は似ていますが、呼び先が決まるタイミングが違います。
パターン詳説
- 53実行可分配束縛 — structとタプルをほどくlet Point { x, y } = p; のように書くと、構造体のフィールドをフィールド名と同じ名前の変数に一度に取り出せます。タプルも let (name, price) = pair; のようにほどけて、パターンの中にパターンを書くネストもできます。
- 54実行可マッチガードと @ 束縛match の腕のパターンの後ろに if 条件を足したものを「マッチガード」と呼び、パターンだけでは表せない条件で腕を絞り込めます。また x @ 1..=9 のように @ を使うと、値が範囲に一致するかを調べつつ、その値自体を変数に束縛して腕の中で使えます。
高度な機能
- 55実行可unsafe — コンパイラの保証を一部外すunsafe ブロックの中では、生ポインタの参照外しなど、コンパイラが安全性を保証できない数種類の操作が許可されます。&num as *const i32 のように生ポインタを「作る」だけなら通常のコードで書けて、「参照外し」だけが unsafe ブロックを必要とします。
- 56実行可演算子オーバーロード — Add トレイトstd::ops::Add トレイトを実装すると、自作の型に + 演算子が使えるようになります。fn add(self, other: Point) -> Point の中に「足す」の意味を定義し、type Output で結果の型を指定します(Output のような「関連型」は次のレッスンで詳しく見ます)。
- 57実行可関連型 — トレイトに型を持たせるトレイト定義の中に type Item; のように書くものを「関連型」と呼びます。実装側が type Item = String; と具体的な型をひとつ決めると、トレイトのメソッドはその型を Self::Item として参照できます。
- 58実行可macro_rules! — 宣言的マクロmacro_rules! は「このパターンの呼び出しを、このコードに展開する」という規則を定義する宣言的マクロです。$( $x:expr ),* は「式がカンマ区切りで0個以上並ぶ」形に一致し、展開側の $( ... )* がその個数ぶん繰り返されます。vec! もこの仕組みで実装されています。