構造体(Struct)とは?
Rustの**構造体(Struct)**は、複数の値をひとつのまとまりとして扱うためのデータ型です。たとえば「ユーザー情報」を表すときに、名前・年齢・メールアドレスをバラバラの変数で管理するより、構造体にまとめると読みやすく扱いやすいコードになります。
他の言語でいえば、JavaやPHPの「クラス」に近い概念ですが、Rustの構造体はデータの保持に特化しており、振る舞い(メソッド)はimplブロックで別途定義する点が特徴です。
基本的な構造体の定義と生成
structキーワードを使って構造体を定義します。
struct User {
name: String,
age: u32,
email: String,
}
fn main() {
let user = User {
name: String::from("Alice"),
age: 30,
email: String::from("alice@example.com"),
};
println!("名前: {}", user.name);
println!("年齢: {}", user.age);
}
フィールドへのアクセスはドット記法(.)を使います。
ミュータブルな構造体
構造体のフィールドを変更したい場合は、変数自体をmutで宣言します。Rustでは構造体全体がミュータブルになる点に注意してください(個別フィールドだけをミュータブルにすることはできません)。
fn main() {
let mut user = User {
name: String::from("Bob"),
age: 25,
email: String::from("bob@example.com"),
};
user.age = 26; // フィールドを更新
println!("誕生日!年齢: {}", user.age);
}
フィールド省略記法とストラクト更新記法
変数名とフィールド名が同じ場合、省略して書けます。また、一部のフィールドだけ変えて新しい構造体を作る「ストラクト更新記法」も便利です。
fn create_user(name: String, email: String) -> User {
User {
name, // 変数名と同じなので省略可
email,
age: 0,
}
}
fn main() {
let user1 = create_user(String::from("Carol"), String::from("carol@example.com"));
// user1をベースにageだけ変えた新しいUser
let user2 = User {
age: 20,
..user1 // 残りのフィールドをuser1からコピー
};
println!("名前: {}, 年齢: {}", user2.name, user2.age);
}
⚠️
..user1を使うとuser1のString型フィールド(nameやuser1は使えなくなります。
implブロックでメソッドを追加する
構造体に振る舞いを持たせるにはimplブロックを使います。
struct Rectangle {
width: f64,
height: f64,
}
impl Rectangle {
// メソッド:第一引数が &self
fn area(&self) -> f64 {
self.width * self.height
}
fn perimeter(&self) -> f64 {
2.0 * (self.width + self.height)
}
fn is_square(&self) -> bool {
self.width == self.height
}
}
fn main() {
let rect = Rectangle {
width: 10.0,
height: 5.0,
};
println!("面積: {}", rect.area());
println!("周囲の長さ: {}", rect.perimeter());
println!("正方形?: {}", rect.is_square());
}
&selfは「この構造体への参照」を意味します。値の読み取りだけなら&self、変更が必要なら&mut selfを使います。
フィールドを変更するメソッド
impl Rectangle {
fn scale(&mut self, factor: f64) {
self.width *= factor;
self.height *= factor;
}
}
fn main() {
let mut rect = Rectangle { width: 4.0, height: 3.0 };
rect.scale(2.0);
println!("拡大後: {}x{}", rect.width, rect.height); // 8x6
}
関連関数(コンストラクタパターン)
selfを引数に取らない関数を関連関数と呼びます。呼び出しには::を使い、Rustでは慣習的にnewという名前のコンストラクタを定義することが多いです。
impl Rectangle {
// 関連関数(コンストラクタ)
fn new(width: f64, height: f64) -> Self {
Rectangle { width, height }
}
fn square(size: f64) -> Self {
Rectangle { width: size, height: size }
}
}
fn main() {
let rect = Rectangle::new(8.0, 6.0);
let sq = Rectangle::square(5.0);
println!("長方形の面積: {}", rect.area());
println!("正方形の面積: {}", sq.area());
}
タプル構造体
フィールド名を省略したい場合はタプル構造体が使えます。
struct Point(f64, f64);
struct Color(u8, u8, u8);
fn main() {
let origin = Point(0.0, 0.0);
let red = Color(255, 0, 0);
println!("座標: ({}, {})", origin.0, origin.1);
println!("赤: R={}, G={}, B={}", red.0, red.1, red.2);
}
フィールドへのアクセスはインデックス(.0, .1...)で行います。
デバッグ出力を使う(#[derive(Debug)])
開発中に構造体の内容を丸ごと出力したいときは、#[derive(Debug)]を付けて{:?}で表示できます。
#[derive(Debug)]
struct User {
name: String,
age: u32,
}
fn main() {
let user = User { name: String::from("Dave"), age: 28 };
println!("{:?}", user);
println!("{:#?}", user); // 見やすい整形表示
}
まとめ
| 機能 | 書き方 |
|---|---|
| 構造体の定義 | struct 名前 { フィールド: 型 } |
| インスタンス生成 | 名前 { フィールド: 値 } |
| メソッド定義 | implブロック内にfn メソッド名(&self) |
| 関連関数 | implブロック内にfn 関数名() |
| タプル構造体 | struct 名前(型, 型) |
| デバッグ出力 | #[derive(Debug)] + {:?} |
Rustの構造体はシンプルながら非常に強力です。implブロックでメソッドを追加し、deriveマクロで便利な機能を自動実装することで、読みやすく安全なコードを書くことができます。まずは小さな構造体を自分で定義して、実際に動かしてみましょう!