Fungsi adalah potongan kode yang diberi nama, menerima nilai masuk, dan bisa mengembalikan hasil. Bayangin dapur Warung Bakso Mas Bimo. Kasir menyerahkan slip "Bakso urat, Rp15.000, 3 porsi", dapur menghitung dan memasak, lalu mengembalikan satu nampan. Dapur cuma dapat fotokopi slipnya: dicoret di dapur pun, slip asli di kasir tetap bersih. Slip itu argumen, dapur itu fungsinya, nampannya return value.
🧪 Tebakan cepat: fungsi
naikkan(m Menu)mengisim.Harga = 18000. Sesudahnaikkan(bakso), barisfmt.Println(bakso.Harga)dimainmencetak15000atau18000? Tebak dulu, jawabannya ada di bagian "Argumen selalu disalin".
Bentuk sebuah fungsi
package main
import "fmt"
func subtotal(harga int, porsi int) int {
return harga * porsi
}
func cetakStruk(nama string, jumlah int) {
fmt.Println(nama, jumlah)
}
func main() {
bayar := subtotal(15000, 3)
cetakStruk("Bakso urat", bayar)
fmt.Println(subtotal(12000, 2))
}
$ go run bakso.go
Bakso urat 45000
24000
func subtotal(harga int, porsi int) int: katafunc, nama fungsinya, lalu parameter di dalam kurung (nama dulu, baru tipe), lalu tipe return value, yaitu tipe hasil yang dikembalikan.return harga * porsimengirim hasilnya balik ke pemanggil dan langsung keluar dari fungsi.subtotal(15000, 3)memanggil fungsinya.15000dan3adalah argumen: nilai yang masuk kehargadanporsi, sesuai urutan. Hasilnya,45000, disimpan dibayar.cetakStrukgak punya tipe return. Ia cuma mengerjakan sesuatu (mencetak), jadi gak ada nilai yang di-return.- Parameter bertipe sama boleh disingkat:
func subtotal(harga, porsi int) int. Bentuk ini yang kamu pakai di latihan:func tambah(a, b int) int,kurang, dankali.kurang(10, 5)mengembalikan5,kurang(4, 9)mengembalikan-5,kali(7, 6)mengembalikan42.
Argumen selalu disalin
Go selalu memberi fungsi salinan argumennya. Struct Menu disalin seluruhnya:
type Menu struct {
Nama string
Harga int
}
func naikkan(m Menu) {
m.Harga = 18000
fmt.Println("di naikkan:", m.Harga)
}
func main() {
bakso := Menu{Nama: "Bakso urat", Harga: 15000}
naikkan(bakso)
fmt.Println("di main:", bakso.Harga)
}
di naikkan: 18000
di main: 15000
m adalah fotokopi bakso. Yang berubah cuma fotokopinya, dan fotokopi itu dibuang begitu naikkan selesai. Jadi main tetap mencetak 15000. Itu jawaban tebakannya.
Supaya fungsi bisa mengubah yang asli, kirim alamatnya:
func naikkan(m *Menu) {
m.Harga = 18000
}
naikkan(&bakso) // sekarang di main: 18000
&baksoadalah alamatbaksodi memori. Nilai yang menyimpan alamat disebut pointer.*Menuadalah tipe "pointer ke Menu".m.Harga = 18000lewat pointer mengubahbaksoyang asli.- Tipe argumen harus cocok dengan parameternya.
naikkan(bakso)ke parameter*Menuditolak:cannot use bakso (variable of struct type Menu) as *Menu value in argument to naikkan.
Variadic: jumlah argumen bebas
... sebelum tipe parameter terakhir artinya "boleh berapa pun":
func total(harga ...int) int {
jumlah := 0
for _, h := range harga {
jumlah += h
}
return jumlah
}
pesanan := []int{15000, 12000, 5000}
fmt.Println(total(15000, 12000, 5000)) // 32000
fmt.Println(total(pesanan...)) // 32000
fmt.Println(total()) // 0
- Di dalam fungsi,
hargaadalah slice[]int. Tiga argumen jadi[15000 12000 5000], tanpa argumen jadi slice kosong. - Sudah punya slice? Tambahkan
...di belakangnya:total(pesanan...). Tanpa titik tiga,total(pesanan)ditolak:cannot use pesanan (variable of type []int) as int value in argument to total.
Fungsi juga nilai: closure
Fungsi bisa disimpan di variabel dan dikembalikan dari fungsi lain. Fungsi yang memakai variabel di luar dirinya disebut closure, dan ia membawa variabel itu ke mana pun:
func penghitung() func() int {
nomor := 0
return func() int {
nomor++
return nomor
}
}
ambil := penghitung()
fmt.Println("ambil:", ambil()) // ambil: 1
fmt.Println("ambil:", ambil()) // ambil: 2
lain := penghitung()
fmt.Println("lain:", lain()) // lain: 1
func() intadalah tipe: "fungsi tanpa parameter yang mengembalikan int".fmt.Printf("%T", ambil)mencetak persis itu.- Tiap panggilan
penghitung()membuatnomorbaru.ambildanlainmasing-masing punyanomorsendiri, dan nilainya bertahan di antara panggilan.
Hasil yang diberi nama
Hasil fungsi boleh diberi nama di signature. Variabelnya langsung ada sejak awal fungsi, berisi zero value, dan return tanpa apa-apa (bare return) mengembalikannya:
func subtotal(harga, porsi int) (hasil int) {
if porsi <= 0 {
return // hasil masih 0
}
hasil = harga * porsi
return
}
fmt.Println(subtotal(15000, 0), subtotal(15000, 3)) // 0 45000
Di fungsi pendek, return hasil yang ditulis jelas lebih gampang dibaca.
⚠️ Jebakan yang sering ketemu
- Lupa
return. Fungsi bertipeintyang cuma mencetak hasilnya gagal compile denganmissing return. - Argumen kurang atau tipenya salah.
subtotal(15000)ditolak dengannot enough arguments in call to subtotal, dansubtotal("15000", 3)dengancannot use "15000" (untyped string constant) as int value in argument to subtotal. - Mengira fungsi mengubah aslinya. Struct dan angka disalin. Pakai pointer (
*Menudan&bakso) kalau memang mau mengubah yang asli. - Isi slice ternyata ikut berubah. Go menyalin slice-nya, tapi salinan itu menunjuk isi yang sama.
harga[0] = 0di dalam fungsi membuatpesananjadi[0 12000 5000].
TL;DR: Fungsi ditulis func nama(parameter tipe) tipeHasil { return ... }, dan parameter bertipe sama boleh disingkat: func tambah(a, b int) int. Argumen masuk sesuai urutan dan selalu disalin, jadi naikkan(m Menu) gak mengubah bakso; kirim &bakso ke parameter *Menu untuk mengubah yang asli. ...int menerima berapa pun argumen sebagai slice, dan slice dikirim dengan total(pesanan...). Fungsi adalah nilai: closure membawa variabelnya sendiri, jadi ambil() mengembalikan 1, 2, 3. Hasil boleh diberi nama, lalu return kosong (bare return) mengembalikannya. Lupa return berarti missing return.