型システム編 - 構造体
難易度: 🟡 中級
所要時間: 30分
📚 この章で学ぶこと
- 構造体の定義と使い方
- フィールドアクセス
- コンストラクタの実装
- ネストした構造体
- 構造体配列
構造体の定義
基本的な定義
struct Point {
int x;
int y;
}
struct Rectangle {
int width;
int height;
string color;
}
struct Person {
string name;
int age;
double height;
}
型のサイズ
構造体のサイズは、全フィールドのサイズの合計です。
struct Point {
int x; // 4 bytes
int y; // 4 bytes
}
// 合計: 8 bytes
構造体の初期化
フィールド初期化
Point p1;
p1.x = 10;
p1.y = 20;
println("Point: ({}, {})", p1.x, p1.y);
デフォルト値
struct Config {
int timeout;
bool debug;
}
int main() {
Config cfg;
// 未初期化フィールドは不定値
cfg.timeout = 30;
cfg.debug = false;
return 0;
}
注意: 現在、構造体リテラル初期化(Rectangle{100, 50, "blue"})は未実装です。
コンストラクタ
デフォルトコンストラクタ
struct Point {
int x;
int y;
}
impl Point {
self() {
self.x = 0;
self.y = 0;
}
}
int main() {
Point origin; // (0, 0)で初期化
println("({}, {})", origin.x, origin.y);
return 0;
}
引数付きコンストラクタ
impl Point {
overload self(int x, int y) {
self.x = x;
self.y = y;
}
}
int main() {
Point p1; // デフォルト: (0, 0)
Point p2(10, 20); // 引数付き: (10, 20)
return 0;
}
複数のコンストラクタ
struct Rectangle {
int width;
int height;
}
impl Rectangle {
// デフォルト: 1x1
self() {
self.width = 1;
self.height = 1;
}
// 正方形
overload self(int size) {
self.width = size;
self.height = size;
}
// 長方形
overload self(int w, int h) {
self.width = w;
self.height = h;
}
}
int main() {
Rectangle r1; // 1x1
Rectangle r2(5); // 5x5
Rectangle r3(3, 4); // 3x4
return 0;
}
メソッドの実装
直接実装 (Inherent Impl)
インターフェースを使わずに、構造体に直接メソッドを定義できます。
self は構造体へのポインタとして扱われるため、フィールドの値を変更できます。
struct Point {
int x;
int y;
}
impl Point {
// コンストラクタ
self(int x, int y) {
this.x = x;
this.y = y;
}
// メソッド(selfは自動的に *Point 型になる)
void move(int dx, int dy) {
// self.x は (*self).x と同等(自動デリファレンス)
self.x += dx;
self.y += dy;
}
void print() {
println("Point({}, {})", self.x, self.y);
}
}
int main() {
Point p(10, 20);
p.print(); // Point(10, 20)
p.move(5, 5); // 状態を変更
p.print(); // Point(15, 25)
return 0;
}
インターフェース実装
特定の振る舞いを共有したい場合は、インターフェースを使用します。
interface Printable {
void print();
}
impl Point for Printable {
void print() {
println("Point({}, {})", self.x, self.y);
}
}
ネストした構造体
構造体フィールド
struct Point {
int x;
int y;
}
struct Line {
Point start;
Point end;
}
int main() {
Line line;
line.start.x = 0;
line.start.y = 0;
line.end.x = 100;
line.end.y = 100;
println("Line from ({}, {}) to ({}, {})",
line.start.x, line.start.y,
line.end.x, line.end.y);
return 0;
}
ネストした初期化
struct Point {
int x;
int y;
}
struct Circle {
Point center;
double radius;
}
int main() {
Circle c;
c.center.x = 50;
c.center.y = 50;
c.radius = 10.0;
return 0;
}
構造体配列
struct Point {
int x;
int y;
}
int main() {
Point[3] points;
points[0].x = 0;
points[0].y = 0;
points[1].x = 10;
points[1].y = 20;
points[2].x = 30;
points[2].y = 40;
// 配列のループ
for (int i = 0; i < 3; i++) {
println("Point[{}]: ({}, {})",
i, points[i].x, points[i].y);
}
return 0;
}
構造体のコピー
値渡し
void modify(Point p) {
p.x = 100; // ローカルコピーを変更
}
int main() {
Point p1(10, 20);
modify(p1);
println("{}", p1.x); // 10(変更されない)
return 0;
}
ポインタ渡し
void modify(Point* p) {
(*p).x = 100; // 元のオブジェクトを変更
}
int main() {
Point p1(10, 20);
modify(&p1);
println("{}", p1.x); // 100(変更される)
return 0;
}
よくある間違い
❌ フィールド名の誤り
struct Point {
int x;
int y;
}
Point p;
// p.z = 10; // エラー: フィールドzは存在しない
❌ 未初期化フィールドの使用
Point p;
// println("{}", p.x); // 警告: 未初期化の可能性
❌ 構造体の直接比較
Point p1(10, 20);
Point p2(10, 20);
// if (p1 == p2) { } // エラー: ==演算子が未定義
// 解決策: with Eq を使うか、operator==を実装
練習問題
問題1: 円の構造体
円を表す構造体を作成し、面積を計算するメソッドを実装してください。
解答例
```cm struct Circle { double x; double y; double radius; } interface Shape { double area(); } impl Circle for Shape { double area() { const double PI = 3.14159; return PI * self.radius * self.radius; } } int main() { Circle c; c.x = 0.0; c.y = 0.0; c.radius = 5.0; println("Area: {:.2}", c.area()); return 0; } ```次のステップ
✅ 構造体の定義と初期化ができる
✅ コンストラクタが実装できる
✅ ネストした構造体が使える
⏭️ 次は Enum型 を学びましょう
関連リンク
最終更新: 2026-02-08