Go言語の構造体
2026/07/28

構造体型

構造体はフィールドの名前付き集合です。

type Point struct {
    X, Y float64
}

複合リテラル

p1 := Point{1, 2}        // Point値(位置で X, Yに対応)
p2 := Point{X: 1, Y: 2}  // Point値(フィールド名付き)
p3 := &Point{X: 1, Y: 2} // *Point

// p3と同じ意味
p := Point{X: 1, Y: 2}
p4 := &p // *Point

p1p2は構造体の値そのものです。p3は複合リテラルで作った値のアドレスを、その場で取った*Pointです。pを用意してp4 := &pとする書き方も同じ意味で、p3p4は同じ型・同じ内容のポインタになります。違いは、p3なら1つの式で初期化とアドレス取得が終わることです。

*Pointを引数に取る関数へ渡すときや、構造体のフィールドがポインタ型のときに&T{...}がよく使われます。ゼロ値へのポインタだけ欲しいときはnew(Point)も使えますが、フィールドを初期値で埋めたいときは&Point{X: 1, Y: 2}の方が意図が読み取りやすいです。

埋め込みは合成であり継承ではない

フィールドに型名だけを書くと埋め込みになります。埋め込みは継承ではなく合成です。埋め込まれたフィールドは外側に昇格し、c.Powerのように名前だけでアクセスできます。埋め込んだ型のメソッドも同様に昇格して、外側から呼び出せます。

type Engine struct{ Power int }
type Car struct {
    Engine
    Model string
}

c := Car{Engine: Engine{Power: 150}, Model: "compact"}
fmt.Println(c.Power)    // 昇格: c.Engine.Powerと同じ
var e Engine = c.Engine // OK
// var e2 Engine = c    // コンパイルエラー: CarはEngineではない

CarEngineとして代入することはできません。型の関係は「中に持っている」だけです。埋め込みを継承と考えると、インターフェース実装の判定を誤解しやすくなります。実装の有無は外側型のメソッド集合で判定されます。

比較

すべてのフィールドが比較可能なときだけ==で比較できます。スライス、マップ、関数を含む構造体は比較できません(マップのキーにも使えません)。

a := Point{X: 1, Y: 2}
b := Point{X: 1, Y: 2}
fmt.Println(a == b) // true

type Record struct {
    Tags []string // スライスを含むため比較不可
}
// var r1, r2 Record
// fmt.Println(r1 == r2) // コンパイルエラー