Setelah mampu mendefinisikan fungsi, pertanyaan berikutnya adalah bagaimana nilai berpindah dari pemanggil ke dalam fungsi. Go memakai satu aturan yang menyentuh semua tipe: passing by value — setiap argumen disalin menjadi variabel baru. Konsekuensi aturan ini berbeda-beda: salinan int sepenuhnya terpisah dari variabel asal, sedangkan slice membawa struktur khusus yang membuat sebagian perubahan tetap terlihat oleh pemanggil. Pada chapter ini kita akan membuktikan ketiga perilaku tersebut dengan program nyata, lalu menutupnya dengan parameter pointer sebagai jalan resmi memutasi variabel pemanggil.

Parameter Formal dan Argumen #

Parameter adalah variabel yang dideklarasikan pada definisi fungsi, sedangkan argumen adalah nilai nyata yang dikirim pada saat pemanggilan. Pada deklarasi naikkanSalin(angka int), nama angka adalah parameter; pada pemanggilan naikkanSalin(skor), isi variabel skor menjadi argumen. Pembagian ini membantu kita berbicara presisi: parameter hidup di dalam fungsi, argumen lahir di sisi pemanggil.

Semua Argumen Disalin #

Go menyalin nilai argumen ke variabel parameter setiap kali fungsi dipanggil — untuk semua tipe, tanpa kecuali. Untuk int, salinan tersebut adalah angka yang sama nilainya tetapi menempati memori terpisah. Fungsi boleh mengubah parameternya seberapa pun; variabel milik pemanggil tidak akan pernah berubah. Tabel berikut merangkum konsekuensi aturan ini untuk tiga bentuk parameter yang akan kita uji:

ParameterYang disalin saat pemanggilanPerubahan di dalam fungsi
angka intNilai bilanganTidak terlihat pemanggil
angka *intNilai alamat memoriTerlihat lewat *angka
buah []stringHeader slice (pointer, len, cap)Elemen terlihat, panjang tidak

Bukti untuk baris pertama tabel ada pada program utuh di bawah: naikkanSalin menaikkan parameternya menjadi 15, namun skor milik main tetap bernilai 5 setelah pemanggilan.

Parameter Pointer untuk Mutasi #

Ketika fungsi perlu mengubah variabel milik pemanggil, parameter dideklarasikan bertipe pointer, misalnya *int, dan pemanggil mengirim alamat variabel dengan operator &. Nilai yang disalin tetap satu — alamatnya — sehingga kedua pihak menunjuk variabel yang sama. Di dalam fungsi, operator * membaca dan menulis isi variabel asli: *angka = *angka + 10 menambah langsung ke skor milik pemanggil. Pola naikkanPointer(&skor) itulah yang membuat skor berubah dari 5 menjadi 15 pada program utuh.

Slice: Header Disalin, Backing Array Dibagi #

Slice adalah wilayah abu-abu yang paling sering mengejutkan pemula. Argumen slice disalin juga, tetapi salinannya berupa header berisi tiga data: alamat backing array, panjang, dan kapasitas. Elemen array yang ditunjuk header tidak ikut disalin, sehingga fungsi dan pemanggil berbagi elemen yang sama. Akibatnya dua hal terjadi sekaligus pada program utuh: ubahElemen mengganti buah[0] menjadi "jeruk" dan pemanggil melihat perubahannya, sedangkan tambahElemen melakukan append pada salinan header — header lokal berubah panjang menjadi 3, tetapi header milik pemanggil tetap panjang 2 sehingga "mangga" tidak pernah terlihat.

String Bersifat Immutable #

String menerapkan aturan lebih ketat: isinya tidak bisa diubah siapa pun, termasuk oleh fungsi pemilik parameternya. Berkas error-string.go di bawah mencoba mengubah huruf pertama parameter nama dan sengaja gagal dikompilasi:

go
package main

import "fmt"

// ubahHuruf sengaja gagal dikompilasi: elemen string bersifat read-only.
func ubahHuruf(nama string) {
	nama[0] = 'B'
}

func main() {
	nama := "siti"
	ubahHuruf(nama)
	fmt.Println(nama)
}

Output yang Dihasilkan:

text
# command-line-arguments
./error-string.go:7:2: cannot assign to nama[0] (neither addressable nor a map index expression)

Untuk “mengubah” string, buat string baru: konversi ke []byte, ubah elemennya, lalu konversi balik.

Program Utuh #

Berkas parameter-demo.go menggabungkan pembuktian salinan int, mutasi lewat pointer, serta dua sisi perilaku slice. Jalankan dari direktori chapter:

bash
go run parameter-demo.go

Output yang Dihasilkan:

text
Di dalam naikkanSalin: 15
Skor pemanggil: 5
Skor setelah naikkanPointer: 15
Setelah ubahElemen: [jeruk pisang]
Di dalam tambahElemen: 3
Setelah tambahElemen: [jeruk pisang]

Mari kita telaah baris demi baris program di atas:

  1. Baris 6 (func naikkanSalin(angka int)): parameter int biasa; fungsi menerima salinan nilai sehingga kenaikan ke 15 hanya hidup di dalam fungsi.
  2. Baris 12 (func naikkanPointer(angka *int)): parameter pointer; *angka = *angka + 10 menulis ke variabel asli milik pemanggil lewat alamat yang disalin.
  3. Baris 18 (buah[0] = "jeruk"): menulis elemen backing array yang dibagi bersama, sehingga pemanggil melihat [jeruk pisang].
  4. Baris 24 (buah = append(buah, "mangga")): append menghasilkan header baru yang hanya berlaku di dalam tambahElemen — panjang lokal 3, panjang pemanggil tetap 2.
  5. Baris 28–41 (isi main): memanggil keempat fungsi secara berpasangan lalu mencetak kondisi skor dan daftar setelahnya, sehingga efek tiap mekanisme terlihat berurutan.

Konsep pointer ini akan dibahas lebih dalam pada chapter Pointer Parameter . Sebelum ke sana, kita akan mempelajari kemampuan khas Go: mengembalikan banyak nilai sekaligus dari satu fungsi.

Referensi #

Berkas Sumber

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

error-string.go

Unduh berkas
go
package main

import "fmt"

// ubahHuruf sengaja gagal dikompilasi: elemen string bersifat read-only.
func ubahHuruf(nama string) {
	nama[0] = 'B'
}

func main() {
	nama := "siti"
	ubahHuruf(nama)
	fmt.Println(nama)
}

parameter-demo.go

Unduh berkas
go
package main

import "fmt"

// naikkanSalin menerima salinan nilai angka; perubahan tidak terlihat pemanggil.
func naikkanSalin(angka int) {
	angka = angka + 10
	fmt.Println("Di dalam naikkanSalin:", angka)
}

// naikkanPointer menerima alamat variabel lalu mengubah isi variabel aslinya.
func naikkanPointer(angka *int) {
	*angka = *angka + 10
}

// ubahElemen mengubah elemen slice; perubahan terlihat oleh pemanggil.
func ubahElemen(buah []string) {
	buah[0] = "jeruk"
}

// tambahElemen melakukan append pada salinan header slice.
// Perubahan panjang tidak terlihat oleh pemanggil.
func tambahElemen(buah []string) {
	buah = append(buah, "mangga")
	fmt.Println("Di dalam tambahElemen:", len(buah))
}

func main() {
	skor := 5
	naikkanSalin(skor)
	fmt.Println("Skor pemanggil:", skor)

	naikkanPointer(&skor)
	fmt.Println("Skor setelah naikkanPointer:", skor)

	daftar := []string{"apel", "pisang"}
	ubahElemen(daftar)
	fmt.Println("Setelah ubahElemen:", daftar)

	tambahElemen(daftar)
	fmt.Println("Setelah tambahElemen:", daftar)
}