Dev Study
← コース一覧

Rust

所有権モデルによってメモリ安全性をコンパイル時に保証するシステムプログラミング言語です。変数や関数の基礎から、所有権・借用、コレクション、エラー処理、トレイト、イテレータまでを1テーマずつ順番に学びます。

公式ドキュメント: The Rust Programming Language

基本文法

  1. 1
    変数と可変性 — let と mut
    Rust では let で変数を宣言します。宣言した変数はデフォルトで不変(イミュータブル)で、あとから値を代入し直すことはできません。書き換えたい変数には let mut のように mut キーワードを付けます。
    実行可
  2. 2
    基本型と型推論
    Rust は静的型付け言語で、整数の i32 や u64、浮動小数点数の f64、真偽値の bool、文字の char などの基本型があります。型は let x: i32 = 5; のようにコロンの後ろに書きますが、多くの場合は右辺から推論されるので省略できます。
    実行可
  3. 3
    シャドーイング — 同じ名前で再宣言
    let で宣言済みの名前をもう一度 let で宣言し直すことを「シャドーイング」と呼びます。再代入と違って mut は不要で、新しい変数が古い変数を覆い隠す形になるため、型を変えることもできます。
    実行可
  4. 4
    関数 — 文と式の違い
    関数は fn で定義し、引数には型注釈が必須で、戻り値の型は -> の後ろに書きます。Rust では「文(セミコロンで終わり値を返さない)」と「式(値を返す)」が区別され、関数本体の最後にセミコロンなしで式を置くと、それが戻り値になります。
    実行可
  5. 5
    if 式 — 条件分岐
    条件分岐は if 条件 { ... } else { ... } と書き、条件に丸括弧は不要です。条件は必ず bool でなければならず、数値を「0 でなければ真」のように扱うことはできません。他言語の癖でつまずきやすいポイントです。
    実行可
  6. 6
    loop と while — 条件による繰り返し
    loop { ... } は無限に繰り返すループで、break で抜けます。break に値を添えると、その値が loop 式全体の値になります。条件を毎回チェックしながら繰り返すには while 条件 { ... } を使います。
    実行可
  7. 7
    for とレンジ — 回数が決まった繰り返し
    for は for i in 0..3 { ... } のように、レンジ(範囲)を順に回るループです。0..3 は 0, 1, 2 を表し、終端を含めたいときは 1..=3 と書きます。
    実行可

所有権

  1. 8
    所有権 — 値はムーブする
    Rust では、すべての値にただ一人の「所有者」となる変数がいます。String のようなヒープ上のデータを別の変数に代入すると、所有権が移動(ムーブ)し、元の変数はその時点で使えなくなります。
    実行可
  2. 9
    Copy と Clone — ムーブしない型・明示的な複製
    i32 や bool、char などの単純な型は Copy という性質を持ち、代入すると値がそのまま複製されます。ムーブが起きないので、代入後も元の変数を使い続けられます。
    実行可
  3. 10
    参照と借用 — &T
    値の所有権を渡さずに中身を読みたいときは、& を付けて参照を作ります。参照を受け取ることを「借用」と呼び、関数の引数を &String のように宣言すれば、呼び出し後も元の変数をそのまま使えます。
    実行可
  4. 11
    可変参照 — &mut T
    借用した値を書き換えたいときは、&mut で可変参照を作ります。参照される側の変数も let mut で宣言されている必要があり、関数側は引数を &mut String のように受け取ります。
    実行可
  5. 12
    スライス — データの一部分への参照
    スライスは、String や配列の「一部分」を指す参照です。&s[0..5] のようにレンジで範囲を指定して作り、データを複製せずに部分列を扱えます。文字列のスライスは &str という型になります。
    実行可

構造体

  1. 13
    struct の定義 — 関連データをまとめる
    struct は複数の値に名前を付けてひとまとめにする型です。struct User { name: String, age: u32 } のようにフィールドを定義し、User { name: ..., age: ... } という形でインスタンスを作り、ドット記法でフィールドにアクセスします。
    実行可
  2. 14
    メソッド — impl ブロックと &self
    構造体に振る舞いを持たせるには、impl 構造体名 { ... } というブロックの中に関数を書きます。第1引数を &self にすると、rect.area() のようにドット記法で呼べるメソッドになります。
    実行可
  3. 15
    関連関数 — ::new とコンストラクタ
    impl ブロックには self を取らない関数も書けます。これを関連関数と呼び、Circle::new(3.5) のように「型名::関数名」で呼び出します。String::from もこの形です。
    実行可

列挙型とパターン

  1. 16
    列挙型 enum
    enum は「取りうる値の種類を列挙する」型です。enum Status { Loading, Success, Error } のように定義し、Status::Loading の形で値を作ります。変数に入るのは、列挙したバリアントのどれか1つだけです。
    実行可
  2. 17
    パターンマッチ match
    match は値をパターンと照合して分岐する式です。match value { パターン => 処理, ... } の形で書き、enum のバリアントごとの分岐や、バリアントが持つデータの取り出しが1つの構文でできます。
    実行可
  3. 18
    if let — 1パターンだけの照合
    「特定のバリアントのときだけ処理したい」場面では、match の代わりに if let パターン = 値 { ... } と書けます。パターンに一致したときだけ中の処理が実行され、バリアントが持つデータも取り出せます。
    実行可

コレクション

  1. 19
    Vec<T> — 可変長の配列
    Vec<T> は要素を増減できる可変長の配列です。vec![10, 20, 30] マクロで初期値つきで作り、push で末尾に追加、numbers[0] のように添字でアクセスします。
    実行可
  2. 20
    String — 伸長できる文字列
    String はヒープ上に文字列を持ち、あとから伸ばせる型です。String::from("...") やリテラルの .to_string() で作り、push_str で連結します。複数の値を組み立てるなら format! マクロが便利です。
    実行可
  3. 21
    HashMap<K, V> — キーと値のペア
    HashMap<K, V> はキーから値を引ける表です。標準ライブラリの std::collections にあるため、ファイル先頭で use std::collections::HashMap; と書いてから使います。insert で登録し、map["key"] のようにキーで取り出せます。
    実行可

エラー処理

  1. 22
    panic! — 回復不能なエラー
    panic! マクロを呼ぶと、メッセージを表示してプログラムがその場で停止します。配列の範囲外アクセスなどでも内部的に panic が起きており、「これ以上続行できない」状態を表す仕組みです。
    実行可
  2. 23
    Option<T> — null の代わり
    Rust には null がなく、「値があるかもしれないし、ないかもしれない」は標準の enum である Option<T> で表します。値があるなら Some(値)、ないなら None で、中身を使うには match などで取り出します。
    実行可
  3. 24
    Result<T, E> — 回復可能なエラー
    失敗しうる処理の結果は、標準の enum である Result<T, E> で表します。成功なら Ok(値)、失敗なら Err(エラー) を返し、呼び出し側は match で両方のケースを処理します。
    実行可
  4. 25
    ? 演算子 — エラーの委譲
    Result を返す式の後ろに ? を付けると、「Ok なら中身を取り出して続行、Err ならその場で関数から Err を return」という処理を1文字で書けます。エラーを呼び出し元に委譲するときの定番構文です。
    実行可

ジェネリクスとトレイト

  1. 26
    ジェネリック関数 — 型をパラメータにする
    fn swap<T, U>(...) のように関数名の後ろに <T> を書くと、T を「あとで決まる型」として使えます。これをジェネリクスと呼び、i32 用・f64 用と同じ関数を型ごとに書き分ける重複をなくせます。
    実行可
  2. 27
    トレイトの定義と実装
    トレイトは「この型はこういうメソッドを持つ」という共通の振る舞いの約束です。trait Greet { fn greet(&self) -> String; } のようにシグネチャだけを定義し、impl Greet for Dog { ... } の形で型ごとに実装します。
    実行可
  3. 28
    トレイト境界 — ジェネリクスに条件を付ける
    ジェネリック関数の中で比較や表示などの操作をするには、<T: PartialOrd> のように「T はこのトレイトを実装していること」という条件を付けます。これをトレイト境界と呼びます。
    実行可

クロージャとイテレータ

  1. 29
    クロージャ — その場で作る無名関数
    クロージャは |引数| 式 という形でその場に書ける無名関数です。let add = |a, b| a + b; のように変数に入れて、普通の関数と同じように呼び出せます。引数や戻り値の型は多くの場合推論されます。
    実行可
  2. 30
    イテレータ — 要素を順に取り出す
    イテレータは「要素を順に取り出せるもの」を表す仕組みです。Vec なら .iter() でイテレータを作り、.next() を呼ぶたびに Some(次の要素) が返り、要素が尽きると None が返ります。
    実行可
  3. 31
    map・filter・collect — 変換パイプライン
    イテレータにはクロージャを受け取るメソッドが用意されています。map は各要素を変換し、filter は条件を満たす要素だけを残し、collect で結果を Vec などのコレクションに集めます。
    実行可