Fungsi yang menerima channel sebagai parameter biasanya hanya membutuhkan separuh kemampuan channel tersebut: produsen hanya mengirim, konsumen hanya menerima. Ketika parameter bertipe chan int polos, kedua fungsi itu sama-sama bisa mengirim dan menerima, sehingga niat program tidak terbaca dari signature dan kesalahan peran hanya ketahuan saat runtime. Go mengatasinya lewat channel direction: tipe chan<- T dan <-chan T mengunci channel menjadi kirim-saja atau terima-saja, dan compiler menolak pelanggaran arah sebelum program sempat berjalan. Chapter ini mempraktikkan kedua arah, lalu menyaksikan pesan error compiler saat arah dilanggar.
Tipe Arah: chan<- dan <-chan #
Arah dituliskan dengan posisi tanda panah terhadap kata chan. Panah yang mengarah ke kanan menuju chan (chan<-) berarti data mengalir masuk ke channel: kirim-saja. Panah yang menjauh dari chan ke kiri (<-chan) berarti data mengalir keluar: terima-saja:
func produksi(ch chan<- int) // hanya bisa: ch <- v
func konsumsi(ch <-chan int) // hanya bisa: v := <-chChannel yang dibuat dengan make selalu dua arah (bidirectional). Konversi ke tipe searah terjadi otomatis saat channel dua arah diberikan sebagai argumen — pemanggil meneruskan ch biasa, dan parameter menerima versi yang dikunci arahnya. Konversi balik dari searah menjadi dua arah tidak diizinkan; begitu arah dikunci, kunci itu permanen di dalam fungsi.
Direction sebagai Kontrak Fungsi #
Berkas channel-direction-demo.go menyusun pola produsen-konsumen lengkap dengan arah terkunci di kedua parameter:
package main
import "fmt"
// produksi hanya boleh mengirim nilai ke ch.
func produksi(ch chan<- int, data []int) {
for _, v := range data {
ch <- v
}
close(ch)
}
// konsumsi hanya boleh menerima nilai dari ch.
func konsumsi(ch <-chan int) []int {
hasil := make([]int, 0, 4)
for v := range ch {
hasil = append(hasil, v)
}
return hasil
}
func main() {
ch := make(chan int)
go produksi(ch, []int{10, 20, 30, 40})
hasil := konsumsi(ch)
total := 0
for _, v := range hasil {
total += v
}
fmt.Println("diterima:", hasil)
fmt.Println("total :", total)
}Output yang Dihasilkan:
diterima: [10 20 30 40]
total : 100Mari kita telaah baris demi baris program di atas:
- Baris 6 (
ch chan<- int): parameterproduksidikunci kirim-saja; di dalam fungsi, statement terima<-chakan gagal kompilasi, sehingga peran produsen terjaga oleh compiler. - Baris 10 (
close(ch)): menutup channel masih sah dari tipe kirim-saja karenacloseadalah wewenang pengirim — konsisten dengan kaidah “hanya pengirim yang boleh menutup”. - Baris 14 (
ch <-chan int): parameterkonsumsidikunci terima-saja; fungsi ini tidak mungkin mengirim data palsu ke channel produsen. - Baris 16–18 (
for v := range ch): konsumsi menerima nilai sampai channel ditutup, mengumpulkan salinan tiap nilai ke slicehasil. - Baris 23–26:
mainmembuat satu channel dua arah lalu membagikannya — menjadi kirim-saja bagiproduksidan terima-saja bagikonsumsimelalui konversi otomatis. - Baris 32–33: hasil dicetak setelah
konsumsikembali, sehingga urutan nilai (dan totalnya) pasti sesuai urutan pengiriman.
Dua detail pattern ini layak diingat. Pertama, signature menjadi dokumentasi: pembaca kode tahu tanpa membuka isi fungsi bahwa produksi adalah sumber data dan konsumsi adalah muara. Kedua, channel mengirim tiap nilai kepada tepat satu penerima — jika dua konsumen membaca channel yang sama, nilai terbagi di antara mereka (bukan disalin ke semua), karena itu pola ini juga menjadi dasar distribusi beban yang dibahas di chapter Worker Pool
.
Pelanggaran Arah Ditolak Saat Kompilasi #
Kekuatan sesungguhnya dari direction adalah waktu penangkapan kesalahan: sebelum program berjalan. Berkas error-direction.go sengaja berisi fungsi berkontrak kirim-saja yang mencoba menerima jawaban:
package main
import "fmt"
// laporan berkontrak hanya mengirim hasil lewat ch.
func laporan(ch chan<- string) {
ch <- "data siap"
balasan := <-ch // salah: menerima dari channel kirim-saja
fmt.Println("balasan:", balasan)
}
func main() {
ch := make(chan string)
go laporan(ch)
fmt.Println(<-ch)
}Jalankan berkas tersebut:
go run error-direction.goOutput yang Dihasilkan:
# command-line-arguments
./error-direction.go:8:15: invalid operation: cannot receive from send-only channel chan<- string ch (variable of type chan<- string)Program gagal dikompilasi dan go run berakhir dengan exit code 1, jadi tidak ada output runtime sama sekali. Lokasi 8:15 menunjuk tepat operasi terima <-ch pada baris kedelapan. Pesan ini adalah umpan balik paling murah yang bisa diberikan sistem: kesalahan arah tertangkap di meja compiler, bukan menjadi deadlock misterius di server produksi. Perbaikannya bukan mengubah tipe parameter, melainkan merancang ulang alur — misalnya memakai channel terima-saja terpisah bila jawaban memang diperlukan.
Referensi #
- Channel types — Spesifikasi Go — aturan resmi tipe arah dan konversinya.
- Concurrency — Effective Go — pola produsen-konsumsi dengan channel.
- Go Concurrency Patterns: Pipelines — The Go Blog — pemakaian direction pada pipeline multi-tahap.
Arah channel menjawab pertanyaan “siapa boleh apa”. Pertanyaan berikutnya dalam program concurrent lebih dinamis: “operasi mana yang siap lebih dulu” — dan untuk itu Go menyediakan statement select pada chapter selanjutnya.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
channel-direction-demo.go
Unduh berkaspackage main
import "fmt"
// produksi hanya boleh mengirim nilai ke ch.
func produksi(ch chan<- int, data []int) {
for _, v := range data {
ch <- v
}
close(ch)
}
// konsumsi hanya boleh menerima nilai dari ch.
func konsumsi(ch <-chan int) []int {
hasil := make([]int, 0, 4)
for v := range ch {
hasil = append(hasil, v)
}
return hasil
}
func main() {
ch := make(chan int)
go produksi(ch, []int{10, 20, 30, 40})
hasil := konsumsi(ch)
total := 0
for _, v := range hasil {
total += v
}
fmt.Println("diterima:", hasil)
fmt.Println("total :", total)
}
error-direction.go
Unduh berkaspackage main
import "fmt"
// laporan berkontrak hanya mengirim hasil lewat ch.
func laporan(ch chan<- string) {
ch <- "data siap"
balasan := <-ch // salah: menerima dari channel kirim-saja
fmt.Println("balasan:", balasan)
}
func main() {
ch := make(chan string)
go laporan(ch)
fmt.Println(<-ch)
}