0Pricing
Go Academy · レッスン

メソッドのポインターレシーバー

ポインターレシーバーによる構造体の状態変更

「メソッドのポインターレシーバー」はCoddyKit上の無料Go Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはGo Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Go Academyコースには全4レッスンが含まれています。

まとめ: 値レシーバーとポインタレシーバー

値レシーバーは構造体のコピーに対して動作します。ポインタレシーバーは元の構造体に対して動作します。メソッドで状態を変更する必要がある場合は、必ずポインタレシーバーを使ってください。

ポインタレシーバーによる変更

構造体を変更できるのはポインタレシーバーだけです:

package main
import "fmt"

type Stack struct{ items []int }

func (s *Stack) Push(v int) { s.items = append(s.items, v) }
func (s *Stack) Pop() int {
    n := len(s.items) - 1
    v := s.items[n]
    s.items = s.items[:n]
    return v
}

func main() {
    var st Stack
    st.Push(1); st.Push(2)
    fmt.Println(st.Pop()) // 2
}

一貫性のルール

ある型のメソッドのいずれかでポインタレシーバーが必要な場合は、すべてのメソッドでポインタレシーバーを使ってください。混在させると、インターフェースを満たす際に問題が起きます:

package main
import "fmt"

type Counter struct{ n int }

func (c *Counter) Inc()   { c.n++ }   // pointer
func (c *Counter) Val() int { return c.n } // pointer too

func main() {
    c := &Counter{}
    c.Inc(); c.Inc()
    fmt.Println(c.Val()) // 2
}

インターフェースを満たす条件

ポインタレシーバーのメソッドは*Tのメソッドセットにのみ属し、Tには属しません。これはインターフェースを満たすかどうかに影響します:

package main
import "fmt"

type Writer interface{ Write([]byte) }

type File struct{ name string }
func (f *File) Write(b []byte) { fmt.Println(string(b)) }

func main() {
    var w Writer = &File{"out.txt"} // OK
    // var w Writer = File{} — compile error
    w.Write([]byte("hello"))
}

アドレスの自動取得

アドレス可能な値に対してポインタレシーバーのメソッドを呼び出すと、Goが自動的にアドレスを取得します:

package main
import "fmt"

type Box struct{ V int }
func (b *Box) Double() { b.V *= 2 }

func main() {
    b := Box{5}
    b.Double()          // Go does (&b).Double()
    fmt.Println(b.V)    // 10
}

アドレスを取得できない値

アドレスを取得できない値(マップ要素や、変数に代入されていない関数の戻り値)に対しては、ポインタレシーバーのメソッドを呼び出せません:

package main

type T struct{ V int }
func (t *T) Set(v int) { t.V = v }

func getT() T { return T{} }

func main() {
    var t T
    t.Set(1)       // OK — t is addressable
    // getT().Set(1) — ERROR: cannot take address of getT()
    _ = t
}

大きな構造体のレシーバー

メソッドを呼び出すたびにコストの高いコピーが発生するのを避けるため、大きな構造体では必ずポインタレシーバーを使ってください:

package main

type Image struct{ pixels [1024][1024]uint8 }

// Value receiver copies 1MB per call!
func (img Image) Bad() uint8 { return img.pixels[0][0] }

// Pointer receiver — zero copy
func (img *Image) Good() uint8 { return img.pixels[0][0] }

func main() { _ = Image{} }

nilポインタレシーバー

ポインタレシーバーのメソッドでは、nilを適切に処理できます:

package main
import "fmt"

type List struct{ head *node }
type node struct{ val int; next *node }

func (l *List) Len() int {
    if l == nil { return 0 }
    count := 0
    for n := l.head; n != nil; n = n.next { count++ }
    return count
}

func main() {
    var l *List
    fmt.Println(l.Len()) // 0
}

埋め込みとポインタレシーバー

ポインタレシーバーを持つ型を埋め込む場合、昇格されたメソッドを利用するには、外側の構造体もポインタとして使う必要があります:

package main
import "fmt"

type Base struct{ V int }
func (b *Base) Set(v int) { b.V = v }

type Derived struct{ Base }

func main() {
    d := &Derived{}
    d.Set(42)           // promoted pointer-receiver method
    fmt.Println(d.V)    // 42
}

ミューテックスのコピー

sync.Mutexを含む構造体は決してコピーしないでください。必ずポインタレシーバーを使うか、ポインタで渡してください。ロック済みのミューテックスをコピーするとデッドロックが発生します:

package main
import "sync"

type Safe struct {
    mu sync.Mutex
    v  int
}

// Correct: pointer receiver
func (s *Safe) Set(v int) {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.v = v
}

func main() { _ = Safe{} }

go vetでミスを検出する

一般的なポインタレシーバーのミス(ミューテックスやロックのコピーなど)を見つけるには、go vet ./...を実行してください。これはgo buildのパイプラインやCIチェックの一部です。

確認問題

ポインタレシーバーのメソッドは、どの型のメソッドセットに属しますか?

まとめ

重要なポイント:

  • ポインタレシーバーは値を変更しますが、値レシーバーは変更しません
  • 1つの型のすべてのメソッドで、同じ種類のレシーバーを使います
  • ポインタレシーバーのメソッドを要求するインターフェースを満たせるのは*Tだけです
  • ミューテックスを含む構造体は決してコピーせず、ポインタレシーバーを使います

練習課題

[]intスライスを使って、ポインタレシーバーのEnqueue(int)、Dequeue() (int, bool)、Len() intメソッドを持つQueue構造体を実装してください。

よくある質問

「メソッドのポインターレシーバー」レッスンは無料ですか?

はい。「メソッドのポインターレシーバー」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Go Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Go Academyコースには全4レッスンが含まれています。

「メソッドのポインターレシーバー」で何を学びますか?

ポインターレシーバーによる構造体の状態変更 ブラウザで直接実行するハンズオンコードでGo Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Go Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのGo Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「メソッドのポインターレシーバー」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このGo Academyレッスンでコードを書いて実行できますか?

はい。すべてのGo Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. ポインターとは
  2. ポインターと関数
  3. メソッドのポインターレシーバー
  4. new()とポインターを使う場面
← Go Academyに戻る