Method di Go adalah fungsi dengan parameter khusus bernama receiver — dan seperti parameter biasa, receiver juga dikirim sebagai salinan. Method dengan value receiver bekerja pada salinan struct, sehingga seluruh mutasi hilang saat method selesai. Method dengan pointer receiver menerima alamat struct asli sehingga dapat mengubah state milik pemanggil secara permanen. Chapter ini mendemonstrasikan perbedaan keduanya pada satu struct Counter, lalu menghubungkan pilihan tersebut dengan aturan method set yang menentukan kecocokan sebuah tipe dengan interface.

Value Receiver Menyalin Struct #

Deklarasi func (c Counter) naikNilai() memberi method tersebut salinan struct Counter. Pernyataan c.Total = c.Total + 1 di dalam method mengubah salinan, bukan struct asli — hasilnya sama sekali tidak terlihat oleh pemanggil. Sebaliknya, deklarasi func (c *Counter) Naik() menjadikan receiver bertipe *Counter: penulisan c.Total di dalam method mengubah field pada struct asli melalui alamatnya. Pointer receiver juga menghindari penyalinan struct besar pada setiap pemanggilan method, alasan kedua yang sama kuatnya dengan kemampuan mutasi.

Contoh Utuh: Counter yang Naik dan yang Tetap Diam #

Program berikut mendefinisikan kedua bentuk receiver pada struct Counter, lalu memanggilnya dari variabel dan dari pointer:

go
package main

import "fmt"

type Counter struct {
	Total int
}

// naikNilai memakai value receiver: method menerima salinan struct.
func (c Counter) naikNilai() {
	c.Total = c.Total + 1 // hanya mengubah salinan
}

// Naik memakai pointer receiver: method menerima alamat struct asli.
func (c *Counter) Naik() {
	c.Total = c.Total + 1 // mengubah milik pemanggil
}

func main() {
	c := Counter{Total: 0}

	c.naikNilai()
	c.naikNilai()
	fmt.Println("setelah naikNilai 2x:", c.Total) // tetap 0

	c.Naik()
	c.Naik()
	fmt.Println("setelah Naik 2x     :", c.Total) // menjadi 2

	p := &c
	p.Naik()                                      // pointer memanggil method pointer receiver secara langsung
	fmt.Println("setelah p.Naik()    :", c.Total) // menjadi 3
}

Jalankan program tersebut dari direktori chapter dengan perintah berikut:

bash
go run pointer-receiver-demo.go

Output yang Dihasilkan:

text
setelah naikNilai 2x: 0
setelah Naik 2x     : 2
setelah p.Naik()    : 3

Mari kita telaah baris demi baris program di atas:

  1. Baris 10-12 (naikNilai): receiver c Counter menghasilkan salinan; kenaikan Total mengenai salinan yang lenyap saat method selesai.
  2. Baris 15-17 (Naik): receiver c *Counter menerima alamat struct asli, sehingga c.Total = c.Total + 1 mengubah state pemanggil secara permanen.
  3. Baris 22-24 (c.naikNilai()): dua pemanggilan ini meninggalkan Total tetap 0 — mutasi salinan tidak pernah terlihat.
  4. Baris 26-28 (c.Naik()): dua pemanggilan ini menaikkan Total menjadi 2 melalui alamat struct.
  5. Baris 30-32 (p.Naik()): variabel p bertipe *Counter memanggil p.Naik() tanpa dereference manual; Total kini bernilai 3.

Gula Sintaksis Alamat Otomatis #

Baris 26 terlihat aneh bagi pemula: variabel c bertipe Counter (nilai), padahal method Naik meminta receiver *Counter. Go menuliskan pemanggilan tersebut sebagai (&c).Naik() — compiler mengambil alamat c secara otomatis karena c adalah variabel yang dapat dialamatkan (addressable). Aturan yang sama berlaku untuk elemen array dan field struct. Sebaliknya, nilai yang tidak dapat dialamatkan — misalnya hasil kembalian fungsi atau elemen map — tidak bisa dipakai memanggil method pointer receiver karena tidak ada alamat yang stabil untuk diambil. Pemanggilan dari pointer seperti p.Naik() tidak membutuhkan konversi apa pun karena tipe p sudah cocok dengan receiver.

Hubungan dengan Method Set #

Pilihan receiver memengaruhi method set, yaitu himpunan method yang dimiliki sebuah tipe. Method set dari Counter hanya memuat method dengan value receiver, sedangkan method set dari *Counter memuat method value receiver sekaligus pointer receiver. Konsekuensinya terasa saat struct memenuhi sebuah interface: interface yang mendaftarkan method pointer receiver hanya dipenuhi oleh *Counter, bukan Counter. Aturan method set beserta contoh kasusnya dibahas tuntas pada chapter Value Receiver dan Pointer Receiver dan Method Set .

Referensi #

Pointer receiver menyelesaikan kebutuhan mutasi milik pemanggil, dan sesekali muncul kebutuhan satu tingkat lebih dalam: fungsi yang mengubah bukan nilai yang ditunjuk pointer, melainkan pointer itu sendiri. Bentuk pointer bertingkat tersebut kita bahas pada chapter berikutnya.

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

pointer-receiver-demo.go

Unduh berkas
go
package main

import "fmt"

type Counter struct {
	Total int
}

// naikNilai memakai value receiver: method menerima salinan struct.
func (c Counter) naikNilai() {
	c.Total = c.Total + 1 // hanya mengubah salinan
}

// Naik memakai pointer receiver: method menerima alamat struct asli.
func (c *Counter) Naik() {
	c.Total = c.Total + 1 // mengubah milik pemanggil
}

func main() {
	c := Counter{Total: 0}

	c.naikNilai()
	c.naikNilai()
	fmt.Println("setelah naikNilai 2x:", c.Total) // tetap 0

	c.Naik()
	c.Naik()
	fmt.Println("setelah Naik 2x     :", c.Total) // menjadi 2

	p := &c
	p.Naik()                                      // pointer memanggil method pointer receiver secara langsung
	fmt.Println("setelah p.Naik()    :", c.Total) // menjadi 3
}