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 // *Pointp1とp2は構造体の値そのものです。p3は複合リテラルで作った値のアドレスを、その場で取った*Pointです。pを用意してp4 := &pとする書き方も同じ意味で、p3とp4は同じ型・同じ内容のポインタになります。違いは、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ではないCarをEngineとして代入することはできません。型の関係は「中に持っている」だけです。埋め込みを継承と考えると、インターフェース実装の判定を誤解しやすくなります。実装の有無は外側型のメソッド集合で判定されます。
比較
すべてのフィールドが比較可能なときだけ==で比較できます。スライス、マップ、関数を含む構造体は比較できません(マップのキーにも使えません)。
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) // コンパイルエラー