メソッドのポインターレシーバー
ポインターレシーバーによる構造体の状態変更
「メソッドのポインターレシーバー」は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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- ポインターとは
- ポインターと関数
- メソッドのポインターレシーバー
- new()とポインターを使う場面