Go言語のメソッド
2026/07/28

func (c Counter) Inc()と値レシーバで書くと、呼び出し元のc.nは増えません。メソッドは型に紐づく関数で、値レシーバとポインタレシーバでメソッド集合が変わり、インターフェース実装の可否にも影響します。

メソッド宣言

メソッドは型に紐づく関数です。レシーバは関数名の前に書きます。

type Rect struct{ W, H float64 }

func (r Rect) Area() float64 {
    return r.W * r.H
}

レシーバ名は型の短い別名として使われます。同じ型のメソッド集合内で一意である必要があります。

値レシーバとポインタレシーバ

レシーバは値Tまたはポインタ*Tです。

func (r Rect) Perimeter() float64 {
    return 2 * (r.W + r.H)
}

func (r *Rect) Scale(f float64) {
    r.W *= f
    r.H *= f
}

値レシーバではメソッド内の変更はコピーに対してのみ有効です。呼び出し元の値を変更するにはポインタレシーバを使います。

代入可能な値へのアドレス取得

変数pが代入可能なら、値型のレシーバメソッド呼び出しp.Method()は、コンパイラによって(&p).Method()と解釈されることがあります。

type Rect struct{ W, H float64 }
func (r Rect) Area() float64     { return r.W * r.H }
func (r *Rect) Scale(f float64)   { r.W *= f; r.H *= f }

r := Rect{W: 3, H: 4}
r.Scale(2) // 代入可能 → 実質 (*Rect).Scale
fmt.Println(r.Area()) // 24

一方、リテラルやmapの要素など代入可能でない値に対して、ポインタレシーバのメソッドは直接呼べません。

// Rect{W: 1, H: 1}.Scale(2) // コンパイルエラー

ポインタと値の使い分け

次のようなときはポインタレシーバが使われます。

  • メソッドがレシーバのフィールドを変更する
  • レシーバが大きく、コピーを避けたい
  • レシーバがスライスやマップなど、内部データを変更する型である

値レシーバは小さな不変型や、値型として扱いたいときに向きます。大きな構造体では値レシーバのたびに全体がコピーされるため、読み取り専用でもサイズが大きければポインタレシーバを検討します。

ポインタレシーバ*Tのメソッドは、レシーバがnilでも呼び出し自体は可能です。メソッド内でフィールドにアクセスするとパニックになります。値レシーバのメソッドをnil*Tから呼ぶと(*p)に展開されるため、フィールド参照時にパニックします。

var p *Rect
// p.Area()   // 値レシーバ: (*p) の展開で nil ならパニック
// p.Scale(2) // ポインタレシーバ: 呼び出しは可能だが、フィールド参照でパニック

レシーバ型の一貫性

同一の型に対して、値レシーバとポインタレシーバのメソッドを混在させないのが慣習です。混在するとメソッド集合が呼び出し側の型(T*Tか)によって変わり、インターフェース実装の可否にも影響します。値型Tのメソッド集合には値レシーバのメソッドが入り、ポインタ型*Tには値レシーバとポインタレシーバの両方が入ります。

メソッド式とメソッド値

メソッドは関数値として扱えます。

r := Rect{W: 3, H: 4}
fn := r.Area       // メソッド値: レシーバがあらかじめ結び付いている
fmt.Println(fn())

area := Rect.Area  // メソッド式: レシーバを第1引数で渡す
fmt.Println(area(r))

埋め込みとメソッド昇格

構造体に別の型を埋め込むと、その型のメソッドが昇格して外側の型から呼び出せます。埋め込みは継承ではなく、フィールドとして内側の値を持ち、名前を省略してメソッドへ届ける仕組みです。

type Engine struct{}
func (e Engine) Start() {}

type Car struct {
    Engine
}

var c Car
c.Start() // Engine.Start が昇格

外側の型に同名メソッドがある場合、外側が優先されます。インターフェースの埋め込みはメソッド集合の合成であり、構造体の埋め込みとは別の仕組みです。埋め込みはフィールドとメソッドの昇格であり、型の置き換え(代入可能性)を生みません。CarEngineとして使うことはできません。