Menu

Fungsi di Golang: Mendeklarasikan, Memanggil, dan Mengirim Fungsi

Cara mendeklarasikan dan memanggil fungsi di Go: parameter, tipe parameter yang dipakai bersama, nilai kembalian, pass by value, dan fungsi sebagai nilai yang bisa disimpan dan dikirim ke mana-mana.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Mendeklarasikan fungsi

Deklarasi fungsi terdiri dari func, sebuah nama, parameter, tipe hasil, dan badan fungsi. Tipe ditulis setelah nama parameter, bukan sebelumnya.

add menerima dua nilai int dan mengembalikan int. greet tidak mengembalikan apa pun, jadi signature-nya tidak punya tipe hasil dan badannya tidak butuh return.

Fungsi yang mendeklarasikan hasil harus diakhiri dengan pernyataan penutup. Kalau ada jalur yang bisa mencapai kurung kurawal penutup tanpa return, compiler berhenti dengan missing return.

Parameter dengan tipe yang sama

Parameter berurutan yang tipenya sama bisa memakai satu nama tipe bersama. func add(a, b int) int artinya persis sama dengan func add(a int, b int) int. Mencampur keduanya juga boleh:

func scale(x, y float64, factor int) (float64, float64)

Di sini x dan y bertipe float64, sedangkan factor bertipe int.

Nilai kembalian

Sebuah fungsi bisa mengembalikan nol, satu, atau beberapa nilai. Beberapa hasil ditulis di dalam tanda kurung, dan pemanggil menerimanya dengan multiple assignment:

Program ini mencetak 3 2. Pasangan (value, error) adalah pemakaian fitur ini yang paling umum di Go, dan punya halamannya sendiri tentang multiple return value.

Argumen disalin

Go mengirim setiap argumen sebagai nilai. Fungsi bekerja pada salinannya, jadi meng-assign nilai ke parameter tidak pernah mengubah variabel milik pemanggil.

Output-nya:

{1 2}
{100 2}
[99 2 3]

Baris ketiga sering mengejutkan orang. Nilai slice adalah header kecil (pointer, panjang, kapasitas). Salinannya menunjuk ke array yang sama di baliknya, jadi menulis s[0] berarti menulis ke elemen milik pemanggil. Tetapi append di dalam fungsi bisa mengalokasikan array baru, dan pemanggil tidak pernah melihat header baru itu. Kalau sebuah fungsi menambah isi slice, kembalikan slice yang baru. Logika yang sama berlaku untuk map: fungsi bisa menambahkan key yang terlihat oleh pemanggil, karena nilai map merujuk ke data bersama.

Pakai parameter pointer saat fungsi harus mengubah variabel milik pemanggil, atau saat nilainya berupa struct besar yang tidak ingin kamu salin di setiap pemanggilan.

Fungsi adalah nilai

Fungsi punya tipe, yang ditulis tanpa nama: func(int, int) int. Kamu bisa menyimpan fungsi di variabel, memasukkannya ke map atau slice, dan mengirimnya ke fungsi lain.

square adalah fungsi anonim yang di-assign ke sebuah variabel. Fungsi anonim juga bisa membaca dan mengubah variabel dari scope di sekitarnya, dan itulah yang membuatnya menjadi closure.

Tipe fungsi bernama membuat signature lebih mudah dibaca saat bentuk yang sama muncul di banyak tempat:

type Transform func(int) int

func apply(xs []int, f Transform) []int

Zero value dari tipe fungsi adalah nil. Memanggil fungsi nil akan panic dengan invalid memory address or nil pointer dereference, jadi periksa callback opsional sebelum memanggilnya. Nilai fungsi hanya bisa dibandingkan dengan nil, tidak pernah dengan sesamanya.

Rekursi

Fungsi bisa memanggil dirinya sendiri. Go tidak punya optimasi tail call, tetapi stack goroutine bertambah sesuai kebutuhan, jadi kedalaman rekursi yang biasa tidak masalah.

for i := range 6 menghitung dari 0 sampai 5 dan butuh Go 1.22 atau lebih baru.

Yang tidak dimiliki fungsi di Go

  • Tidak ada overloading. Dua fungsi dalam satu package tidak bisa memakai nama yang sama, meskipun parameternya berbeda.
  • Tidak ada argumen default atau argumen bernama. Setiap parameter dikirim di setiap pemanggilan, secara berurutan. Struct konfigurasi memberimu field yang bernama dan opsional: NewServer(Config{Port: 8080}).
  • Tidak ada fungsi bernama yang bersarang. Di dalam badan fungsi, kamu hanya bisa mendeklarasikan fungsi anonim, biasanya di-assign ke sebuah variabel.

Daftar argumen dengan panjang yang bervariasi didukung lewat fungsi variadic.

Fungsi yang diekspor dan tidak diekspor

Fungsi yang namanya diawali huruf kapital (Parse) diekspor dan bisa dipanggil dari package lain. Nama dengan huruf kecil (parse) hanya terlihat di dalam package-nya sendiri. Tidak ada kata kunci public atau private; huruf kapital adalah satu-satunya aturan.

Kesalahan umum

  • Mengharapkan perubahan parameter sampai ke pemanggil. Meng-assign nilai ke parameter hanya mengubah salinannya. Kembalikan nilai yang baru atau terima pointer.
  • Melakukan append di dalam fungsi dan mengabaikan hasilnya. func add(s []int) { s = append(s, 1) } tidak berpengaruh pada panjang slice milik pemanggil. Tulis func add(s []int) []int dan pakai s = add(s).
  • Hasil yang tidak dipakai. Go membolehkan mengabaikan nilai kembalian. go vet tidak menandai error yang dibuang, tetapi linter seperti errcheck (bagian dari golangci-lint) menandainya. Pakai _ = hanya saat mengabaikannya memang sebuah keputusan.

Pertanyaan yang sering diajukan

Bagaimana cara mendefinisikan fungsi di Go?

Pakai kata kunci func, sebuah nama, daftar parameter dengan tipe setelah namanya, dan tipe hasilnya: func add(a, b int) int { return a + b }. Fungsi tanpa hasil tidak menulis tipe, dan fungsi dengan beberapa hasil menuliskannya di dalam tanda kurung: func divmod(a, b int) (int, int).

Apakah Go mengirim argumen sebagai nilai atau referensi?

Selalu sebagai nilai. Fungsi menerima salinan dari setiap argumen. Untuk int atau struct, salinan itu terpisah dari variabel milik pemanggil. Slice, map, channel, dan pointer adalah nilai kecil yang merujuk ke data bersama, jadi perubahan pada elemen yang ditunjuknya terlihat oleh pemanggil, sedangkan meng-assign ulang parameternya sendiri tidak. Kirim pointer (*T) saat fungsi harus mengubah variabel milik pemanggil.

Apakah fungsi bisa dikirim sebagai parameter di Go?

Bisa. Fungsi adalah nilai dengan tipe seperti func(int) int. Deklarasikan parameter bertipe itu dan kirim fungsi apa pun yang signature-nya cocok, baik yang bernama maupun anonim: func apply(xs []int, f func(int) int).

Apakah Go mendukung function overloading atau parameter default?

Tidak. Setiap nama fungsi di dalam satu package harus unik, dan setiap parameter harus dikirim di setiap pemanggilan. Penggantinya yang biasa adalah nama yang berbeda (NewServer, NewServerWithTLS), struct konfigurasi, parameter variadic, atau functional options.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai