Go言語の構造体
2026/09/13

構造体型

構造体は、名前と型を持つフィールドを並べた型です。フィールドは宣言した順に並び、名前は重複できません。同じ型のフィールドはX, Y float64のように1行にまとめられます。フィールド名も識別子なので、先頭が大文字なら他パッケージから参照できます。フィールドにアクセスするときは、値.フィールド名の形式で書きます。

構造体型の変数を定義して何も代入しない場合、各フィールドはゼロ値で埋まります。

type Point struct {
    X, Y float64
}

var p Point
p.X = 1
fmt.Println(p.X) // 1

_はブランク識別子で、フィールド名にも使えますが、名前として参照できないためp._とは書けません。同じ構造体に何度でも書くことができ、領域だけ確保するパディングの用途で使用できます。

type Header struct {
    Flag uint8
    _    [3]byte
    Size uint32
}

構造体リテラル

フィールドをまとめて初期化するときは構造体リテラルを使います。

p1 := Point{1, 2}        // Pointの値
p2 := Point{X: 1, Y: 2}  // Pointの値
p3 := &Point{X: 1, Y: 2} // *Point

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

p1とp2は構造体型の値です。p3は構造体リテラルのアドレスをその場で取った*Pointです。pを用意してp4 := &pとする書き方も同じ意味で、p3とp4は同じ型・同じ内容のポインタになります。

*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)    // 昇格

比較

すべてのフィールドの型が比較可能なときだけ==と!=が使えます。スライス、マップ、関数を含む構造体は比較できません。等価なのは、型が同じで、対応する_以外のフィールドがすべて等しいときです。

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

c := Point{X: 1, Y: 3}
fmt.Println(a == c)     // false

type Vertex struct {
    X, Y float64
}
// fmt.Println(a == Vertex{X: 1, Y: 2})     // コンパイルエラー: 型が違う
fmt.Println(a == Point(Vertex{X: 1, Y: 2})) // true

type Record struct {
    Tags []string
}
var r1, r2 Record
// fmt.Println(r1 == r2) // コンパイルエラー: 比較できないフィールドがある