Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

第2章 基本文法と型システム

Rust は静的型付け言語であり、すべての変数や値の型がコンパイル時に確定します。

型推論が非常に強力であるため、多くの場合で型の明示を省略しながらも、厳格な型安全性を享受できます。本章では、Rust の基本文法、変数と可変性、基本データ型、制御フロー、そしてデータ設計の中心となる構造体と列挙型を学びます。

2.1 変数宣言と可変性

Rust の変数は、デフォルトで 不変(Immutable) です。一度値を束縛すると、後から変更できません。

不変変数と可変変数(mut)

値を変更したい場合は、明示的に mut キーワードを付与します。

fn main() {
    let x = 5;
    // x = 6; // コンパイルエラー:不変変数には再代入できない

    let mut y = 10;
    println!("変更前: {y}");
    y = 20; // OK:可変変数なので変更可能
    println!("変更後: {y}");
}

意図しない値の書き換えを防ぐため、可能な限り不変(let)を使い、値の変更がどうしても必要な場合のみ mut を付与するのが Rust の基本設計です。

定数(const)とシャドーイング

定数は const キーワードで宣言し、常に型注釈が必要です。また、プログラムの実行期間中ずっと有効で、計算結果などを束縛することはできません。

#![allow(unused)]
fn main() {
const MAX_USERS: u32 = 100_000;
}

同じスコープ内で同じ名前の変数を再宣言することを「シャドーイング(Shadowing)」と呼びます。元の値を変換して新しい型にしたいときなどに便利です。

#![allow(unused)]
fn main() {
let spaces = "   "; // 文字列型(&str)
let spaces = spaces.len(); // 数値型(usize)へ変換して再束縛
}

2.2 基本データ型

Rust の基本的なデータ型は、「スカラー型」と「複合型」の 2 つに分類されます。

スカラー型(単一の値を表す型)

  • 整数型: 符号付き(i8, i16, i32, i64, i128, isize)と符号なし(u8, u16, u32, u64, u128, usize)。デフォルトは i32 です。

  • 浮動小数点型: f32 と f64。デフォルトは f64 です。

  • 真偽値型: bool(true または false)。

  • 文字型: char(Unicode スカラー値を表し、4 バイトのサイズを持ちます。日本語や絵文字も 1 文字として扱えます)。

#![allow(unused)]
fn main() {
let age: u32 = 25;
let rate: f64 = 3.14;
let is_active: bool = true;
let icon: char = '🦀';
}

複合型(複数の値をまとめる型)

  • タプル: 異なる型の複数の値を固定長でまとめる型です。

  • 配列: 同じ型の複数の値を固定長でまとめる型です(可変長のリストには後述する Vec を使います)。

#![allow(unused)]
fn main() {
// タプルの宣言と要素へのアクセス
let person: (&str, u32) = ("Alice", 20);
println!("名前: {}, 年齢: {}", person.0, person.1);

// 配列の宣言(要素数 3、型は i32)
let scores: [i32; 3] = [80, 95, 70];
println!("先頭の点数: {}", scores[0]);
}

2.3 関数と制御フロー

関数と「文」と「式」

関数は fn キーワードで定義します。引数と戻り値には必ず型を明記します。

#![allow(unused)]
fn main() {
fn add(a: i32, b: i32) -> i32 {
    a + b // セミコロンを付けないことで「式」となり、関数の戻り値になる
}
}

Rust は 式指向(Expression-based) の言語です。文(Statement)は値を返さず末尾にセミコロン ; を付けます。一方、式(Expression)は値を評価して返します。ブロック {} の最後の式が、そのままそのブロックの評価値になります。

条件分岐(if 式)

if も式であるため、変数に直接結果を代入できます。

#![allow(unused)]
fn main() {
let number = 7;
let message = if number % 2 == 0 {
    "偶数です"
} else {
    "奇数です"
};
println!("{message}");
}

繰り返し(loop, while, for)

#![allow(unused)]
fn main() {
// 無限ループ(break で値を返すことも可能)
let mut count = 0;
let result = loop {
    count += 1;
    if count == 10 {
        break count * 2;
    }
};

// while ループ
let mut n = 3;
while n > 0 {
    n -= 1;
}

// for ループ(イテレータや範囲指定を走査する最も安全で高速な繰り返し)
for i in 1..=5 {
    println!("カウント: {i}");
}
}

2.4 構造体(struct)とメソッド

複数の関連するデータを 1 つの名前付きグループにまとめるには構造体を使います。

// 構造体の定義
struct User {
    username: String,
    email: String,
    active: bool,
}

// メソッドと関連関数の定義
impl User {
    // 関連関数(コンストラクタ的な生成処理。self を取らない)
    fn new(username: &str, email: &str) -> Self {
        Self {
            username: username.to_string(),
            email: email.to_string(),
            active: true,
        }
    }

    // メソッド(インスタンスを参照する &self を取る)
    fn deactivate(&mut self) {
        self.active = false;
    }
}

fn main() {
    let mut user = User::new("taro", "taro@example.com");
    println!("ユーザー名: {}", user.username);
    user.deactivate();
}

2.5 列挙型(enum)とパターンマッチング

Rust の enum は、各バリアントに異なるデータを持たせることができる非常に表現力の高い型です。

#![allow(unused)]
fn main() {
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
}
}

match 式による完全な網羅チェック

match 式を使うと、すべてのパターンを漏れなく処理できます。未処理のケースがあるとコンパイルエラーになります。

#![allow(unused)]
fn main() {
fn handle_message(msg: Message) {
    match msg {
        Message::Quit => {
            println!("終了イベントです");
        }
        Message::Move { x, y } => {
            println!("移動: x={x}, y={y}");
        }
        Message::Write(text) => {
            println!("テキスト出力: {text}");
        }
    }
}
}

if let 式

特定の 1 つのパターンのみに関心がある場合は、if let 構文で簡潔に記述できます。

#![allow(unused)]
fn main() {
let msg = Message::Write("こんにちは".to_string());
if let Message::Write(text) = msg {
    println!("メッセージを受信: {text}");
}
}