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:
| Parameter | Yang disalin saat pemanggilan | Perubahan di dalam fungsi |
|---|---|---|
angka int | Nilai bilangan | Tidak terlihat pemanggil |
angka *int | Nilai alamat memori | Terlihat lewat *angka |
buah []string | Header 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:
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:
# 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:
go run parameter-demo.goOutput yang Dihasilkan:
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:
- Baris 6 (
func naikkanSalin(angka int)): parameterintbiasa; fungsi menerima salinan nilai sehingga kenaikan ke15hanya hidup di dalam fungsi. - Baris 12 (
func naikkanPointer(angka *int)): parameter pointer;*angka = *angka + 10menulis ke variabel asli milik pemanggil lewat alamat yang disalin. - Baris 18 (
buah[0] = "jeruk"): menulis elemen backing array yang dibagi bersama, sehingga pemanggil melihat[jeruk pisang]. - Baris 24 (
buah = append(buah, "mangga")):appendmenghasilkan header baru yang hanya berlaku di dalamtambahElemen— panjang lokal3, panjang pemanggil tetap2. - Baris 28–41 (isi
main): memanggil keempat fungsi secara berpasangan lalu mencetak kondisiskordandaftarsetelahnya, 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 #
- Function calls — Go Spec — aturan evaluasi dan penyalinan argumen.
- A Tour of Go: more types — latihan resmi pointer, slice, dan string.
- Frequently Asked Questions — Go — jawaban resmi berbagai perilaku parameter.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
error-string.go
Unduh berkaspackage 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 berkaspackage 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)
}