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

構造体(Struct)

関連するデータをまとめる「構造体」について学びます。

構造体とは

構造体は複数の関連するデータをまとめた型です。

#![allow(unused)]
fn main() {
struct User {
    username: String,
    email: String,
    age: u32,
    active: bool,
}
}

構造体の定義と使用

定義

#![allow(unused)]
fn main() {
struct Point {
    x: f64,
    y: f64,
}
}

インスタンスの作成

fn main() {
    let p = Point {
        x: 3.0,
        y: 4.0,
    };

    println!("座標: ({}, {})", p.x, p.y);
}

フィールドへのアクセス

fn main() {
    let mut user = User {
        username: String::from("太郎"),
        email: String::from("taro@example.com"),
        age: 25,
        active: true,
    };

    println!("名前: {}", user.username);

    // mutなら変更可能
    user.age = 26;
}

フィールド初期化省略記法

変数名とフィールド名が同じなら省略できます。

#![allow(unused)]
fn main() {
fn create_user(username: String, email: String) -> User {
    User {
        username,  // username: username の省略
        email,     // email: email の省略
        age: 0,
        active: true,
    }
}
}

構造体更新構文

既存のインスタンスから一部だけ変えた新しいインスタンスを作れます。

fn main() {
    let user1 = User {
        username: String::from("太郎"),
        email: String::from("taro@example.com"),
        age: 25,
        active: true,
    };

    let user2 = User {
        email: String::from("jiro@example.com"),
        ..user1  // 残りはuser1から
    };

    // 注意: user1.usernameはムーブされたので使えない
    // println!("{}", user1.username);  // エラー!
    println!("{}", user1.age);  // OK(Copyトレイト)
}

タプル構造体

フィールド名のない構造体です。

struct Color(u8, u8, u8);
struct Point3D(f64, f64, f64);

fn main() {
    let black = Color(0, 0, 0);
    let origin = Point3D(0.0, 0.0, 0.0);

    println!("R: {}", black.0);
    println!("x: {}", origin.0);
}

ユニット様構造体

フィールドを持たない構造体です。トレイト実装に使います。

struct AlwaysEqual;

fn main() {
    let _subject = AlwaysEqual;
}

メソッドの定義(impl)

構造体に関連する関数をimplブロックで定義します。

struct Rectangle {
    width: u32,
    height: u32,
}

impl Rectangle {
    // メソッド(&selfを受け取る)
    fn area(&self) -> u32 {
        self.width * self.height
    }

    // 可変メソッド(&mut selfを受け取る)
    fn double(&mut self) {
        self.width *= 2;
        self.height *= 2;
    }

    // 関連関数(selfを受け取らない)
    fn square(size: u32) -> Rectangle {
        Rectangle {
            width: size,
            height: size,
        }
    }
}

fn main() {
    let mut rect = Rectangle {
        width: 30,
        height: 50,
    };

    println!("面積: {}", rect.area());

    rect.double();
    println!("2倍後の面積: {}", rect.area());

    let sq = Rectangle::square(10);
    println!("正方形の面積: {}", sq.area());
}

&self、&mut self、self の違い

引数意味用途
&self不変借用読み取りのみ
&mut self可変借用値を変更
self所有権を取得インスタンスを消費

複数のimplブロック

#![allow(unused)]
fn main() {
impl Rectangle {
    fn area(&self) -> u32 {
        self.width * self.height
    }
}

impl Rectangle {
    fn can_hold(&self, other: &Rectangle) -> bool {
        self.width > other.width && self.height > other.height
    }
}
}

デバッグ出力

#[derive(Debug)]を使うと、構造体を簡単に表示できます。

#[derive(Debug)]
struct Rectangle {
    width: u32,
    height: u32,
}

fn main() {
    let rect = Rectangle {
        width: 30,
        height: 50,
    };

    println!("{:?}", rect);   // Rectangle { width: 30, height: 50 }
    println!("{:#?}", rect);  // 整形表示
}

実践例: ユーザー管理

#[derive(Debug)]
struct User {
    id: u32,
    username: String,
    email: String,
}

impl User {
    fn new(id: u32, username: String, email: String) -> User {
        User { id, username, email }
    }

    fn display(&self) {
        println!("ID: {}, 名前: {}", self.id, self.username);
    }

    fn update_email(&mut self, new_email: String) {
        self.email = new_email;
    }
}

fn main() {
    let mut user = User::new(1, String::from("太郎"), String::from("taro@example.com"));
    user.display();

    user.update_email(String::from("newtaro@example.com"));
    println!("新しいメール: {}", user.email);
}

まとめ

概念説明
構造体関連データをまとめる
implメソッドを定義
&self読み取りメソッド
&mut self変更メソッド
関連関数Self::func()で呼ぶ
#[derive(Debug)]デバッグ出力を有効化

確認テスト

Q1. メソッド定義で&selfを使う場合、何ができる?

Q2. Rectangle::square(10)のような呼び出しは何と呼ばれる?

Q3. 以下のコードの出力は?
impl Counter { fn increment(&mut self) { self.value += 1; } } let mut c = Counter { value: 0 }; c.increment(); c.increment(); println!("{}", c.value);

Q4. 以下のコードがコンパイルエラーになる理由は?
impl Point { fn move_by(&self, dx: f64, dy: f64) { self.x += dx; self.y += dy; } }

Q5. 構造体に#[derive(Debug)]を付けると何ができる?


次のドキュメント: 02_enums_pattern.md