Zenn
🐉

【Rust】構造体を理解する

2025/03/21に公開

Rsutの構造体(Struct)は複数の関連する値をひとつのデータ型を名前をつけて独自のデータ型として扱うための仕組みです。
この記事では、構造体の使い方、種類、設計のポイントについて解説します。

構造体とは

構造体(Struct)は複数の関連する値をひとつのデータ型を名前をつけて独自のデータ型として管理する仕組みです。他のプログラミング言語の「クラス」に似ていますが、Rustの構造体は単なるデータの集まりを表すだけであり、メソッドを直接持つことはできません。しかし、impl を使うことで、構造体に関連するメソッドを定義できます。

構造体の種類

Rustでは3種類の構造体が用意されています。

  • 名前付きフィールド構造体
  • タプル構造体
  • ユニット構造体

名前付きフィールド構造体(Named Struct)

名前付きフィールド構造体は、最も一般的な構造体で、各フィールドに名前をつけてデータを管理できます。
例えば、RPGのキャラクター情報を表す際に、以下のような構造体を持つことができます。

struct Character {
    name: String, // キャラクター名
    char_type: String, // キャラクターの属性
    level: u8, // レベル
    hp: u8, // HP
    attack: u8, // 攻撃力
    defense: u8, // 防御力
}

fn main() {
    let flame_dragon = Character {
        name: String::from("フレイムドラゴン"),
        char_type: String::from("炎"),
        level: 25,
        hp: 90,
        attack: 100,
        defense: 75,
    };

    println!("CharacterName: {}", flame_dragon.name);
    println!("CharacterType: {}", flame_dragon.char_type);
}

タプル構造体(Tuple Struct)

タプル構造体はフィールド名を持たず、データの順番だけで管理する構造体です。そのため、名前をつける必要がないシンプルなデータ構造(例:座標やRGBデータ)に適しています。
例えば、RGBカラー情報を表す際に、以下のような構造体を持つことができます。

struct RGB(u8, u8, u8);

fn main() {
    let orange = RGB(255, 165, 0);
    println!("RGB({}, {}, {})", orange.0, orange.1, orange.2);
}

ユニット構造体(Unit-like Struct)

ユニット構造体は、フィールドを持たない特別な構造体です。
これは、特定の型であることを示したり、トレイトを実装するためのマーカーとして使われたりします。

struct Marker;

fn main() {
    let _m = Marker;
}

構造体とメソッド

Rustの構造体自体にはメソッドを持たせることはできませんが、impl キーワードを使うことで、関連するメソッドを追加できます。また、Rustのメソッドは通常の関数に似ていますが、第一引数に self を指定することで、構造体のデータにアクセスできます。

implを使ったメソッドの追加

メソッドの基本形として、Character構造体に、 greet というメソッドを追加します。
ポイント

  • impl Character の中で fn greet(&self) を定義している。
  • &self を第一引数に取ることで、このメソッドは Character のインスタンスを参照できる。
  • self.name を使って構造体のデータを取得できる。
struct Character {
    name: String,
}

impl Character {
    fn greet(&self) {
        println!("こんにちは!私は{}です。", self.name);
    }
}

fn main() {
    let alice = Character {
        name: String::from("Alice"),
    };

    alice.greet();
}

上記のように、Character構造体に対して、impl キーワードを使って Character構造体に関連するメソッドを定義しました。

self, &self, &mut selfの違い

Rustのメソッドでは、第一引数に self を指定しますが、以下の3種類の方法があります。

種類 説明 用途
self 所有権を消費する(このインスタンスは使えなくなる) - 新しいインスタンスを作成する
&self 共有参照(読み取り専用でデータを取得できる) - インスタンスの情報を表示する
&mut self 可変参照(データを変更できる) - インスタンスのフィールドを変更する

new によるインスタンス生成

Rustでは new を定義することで、構造体のインスタンスを簡単に作成できます。Javaなどの
コンストラクタ的な役割を果たします。

struct Character {
    name: String,
}

impl Character {
    fn new(name: &str) -> Self {
        Self {
            name: name.to_string(),
        }
    }

    fn greet(&self) {
        println!("こんにちは!私は{}です。", self.name);
    }
}

fn main() {
    let alice = Character::new("Alice");

    alice.greet();
}

関連関数

impl ブロック内に self を引数に取らない関数を定義することもできます。 これは、構造体に関連付けられているので、関連関数と呼ばれます。関連関数は関数であり、対象となる構造体のインスタンスが存在しないため、メソッドではありません。
上記の解説で、new について取り上げましたが、これも関連関数です。

実践! Character構造体を使った例

use rand::{Rng, rng};

struct Character {
    name: String, // キャラクター名
    char_type: String, // キャラクターの属性
    level: u8, // レベル
    hp: u8, // HP
    attack: u8, // 攻撃力
    defense: u8, // 防御力
}

impl Character {
    fn new(name: &str, char_type: &str, level: u8, hp: u8, attack: u8, defense: u8) -> Self {
        Self {
            name: name.to_string(),
            char_type: char_type.to_string(),
            level,
            hp,
            attack,
            defense,
        }
    }

    // キャラクターの情報を表示する
    fn show_status(&self) {
        println!(
            "{} (属性: {}) - Lv:{} | HP:{} | 攻撃力:{} | 防御力:{}",
            self.name, self.char_type, self.level, self.hp, self.attack, self.defense
        );
    }

    // 攻撃アクション(相手のHPを減らす)
    fn attack_enemy(&self, enemy: &mut Character) {
        let mut rng = rng();
        let random_factor: f64 = rng.random_range(0.85..=1.05); // 乱数(0.85 ~ 1.15)

        let base_damage = (((10.0 * (self.level as f64) + 20.0) * (self.attack as f64) / (enemy.defense as f64)) / 20.0 + 2.0) * random_factor;
        let damage = base_damage.floor() as u8;

        enemy.hp = enemy.hp.saturating_sub(damage);
        println!("⚔️ {} が {} に {} ダメージを与えた!", self.name, enemy.name, damage);
    }

    // 回復アクション(HPを回復する)
    fn heal(&mut self, amount: u8) {
        self.hp += amount;
        println!("{} は {} 回復した! (HP: {})", self.name, amount, self.hp);
    }
}

fn main() {
    let mut flame_dragon = Character::new(
        "フレイムドラゴン",
        "炎",
        25,
        90,
        100,
        75,
    );

    let mut rock_dragon = Character::new(
        "ロックドラゴン",
        "岩",
        20,
        85,
        90,
        100,
    );

    flame_dragon.show_status();
    rock_dragon.show_status();

    println!("\n----------------------------\n");

    flame_dragon.attack_enemy(&mut rock_dragon);
    rock_dragon.attack_enemy(&mut flame_dragon);

    println!("\n----------------------------\n");

    flame_dragon.attack_enemy(&mut rock_dragon);
    rock_dragon.attack_enemy(&mut flame_dragon);

    println!("\n----------------------------\n");

    flame_dragon.attack_enemy(&mut rock_dragon);
    rock_dragon.attack_enemy(&mut flame_dragon);

    println!("\n----------------------------\n");

    flame_dragon.show_status();
    rock_dragon.show_status();

    println!("\n----------------------------\n");

    flame_dragon.heal(10);

    println!("\n----------------------------\n");

    flame_dragon.show_status();
    rock_dragon.show_status();
}
出力
フレイムドラゴン (属性: 炎) - Lv:25 | HP:90 | 攻撃力:100 | 防御力:75
ロックドラゴン (属性: 岩) - Lv:20 | HP:85 | 攻撃力:90 | 防御力:100

----------------------------

⚔️ フレイムドラゴン が ロックドラゴン に 13 ダメージを与えた!
⚔️ ロックドラゴン が フレイムドラゴン に 14 ダメージを与えた!

----------------------------

⚔️ フレイムドラゴン が ロックドラゴン に 14 ダメージを与えた!
⚔️ ロックドラゴン が フレイムドラゴン に 14 ダメージを与えた!

----------------------------

⚔️ フレイムドラゴン が ロックドラゴン に 14 ダメージを与えた!
⚔️ ロックドラゴン が フレイムドラゴン に 15 ダメージを与えた!

----------------------------

フレイムドラゴン (属性: 炎) - Lv:25 | HP:47 | 攻撃力:100 | 防御力:75
ロックドラゴン (属性: 岩) - Lv:20 | HP:44 | 攻撃力:90 | 防御力:100

----------------------------

フレイムドラゴン は 10 回復した! (HP: 57)

----------------------------

フレイムドラゴン (属性: 炎) - Lv:25 | HP:57 | 攻撃力:100 | 防御力:75
ロックドラゴン (属性: 岩) - Lv:20 | HP:44 | 攻撃力:90 | 防御力:100

まとめ

  • Rustの構造体はデータをまとめるための仕組みで、impl を使うことでメソッドを追加できる
  • self の3つの種類(self, &self, &mut self)を理解すると、適切なメソッド設計ができる
  • new メソッドを定義すると、インスタンスの作成が簡単になる

Discussion

ログインするとコメントできます