Program Go yang kita tulis sejauh ini masih berupa satu berkas main.go. Begitu program bertumbuh, mempertahankan seluruh kode dalam satu berkas membuat pembacaan dan pengujian menjadi sulit. Go mengatur kode dalam unit bernama package yang dijalin lewat pernyataan import. Pada chapter ini kita akan membedah hubungan direktori dengan package, pembentukan import path, perbedaan package main dengan package library, serta alasan import cycle ditolak compiler.
Package Adalah Direktori #
Aturan paling mendasar di Go: satu package tinggal di satu direktori, dan satu direktori hanya boleh memuat satu package. Semua berkas .go di direktori yang sama wajib menuliskan nama package identik pada baris pertamanya. Nama package secara konvensi sama dengan nama direktorinya, ditulis huruf kecil (contoh: penyapa, bukan Penyapa).
package penyapa
func Salam(nama string) string {
return "Halo, " + nama + "!"
}Fragmen di atas memperlihatkan bentuk awal berkas penyapa/penyapa.go. Klausa package penyapa menjadikan seluruh fungsi di berkas ini — termasuk Salam — anggota package penyapa. Berkas lain di direktori yang sama wajib menuliskan klausa yang sama; nama yang berbeda memicu galat kompilasi. Dengan begitu, struktur direktori menjadi peta pembagian kode.
Import Path: Module Path Plus Subdirektori #
Ketika kita menulis import "fmt", compiler mencari package fmt di standard library. Untuk package milik module sendiri, import path dibentuk dari module path di go.mod ditambah subdirektori package. Module contoh.com/katalog dengan direktori penyapa menghasilkan import path contoh.com/katalog/penyapa.
Formula ini berlaku konsisten untuk module lokal maupun publik:
| Asal package | Import path |
|---|---|
| Standard library | fmt, strings, net/http |
Module contoh.com/katalog, direktori penyapa | contoh.com/katalog/penyapa |
Module publik github.com/google/uuid, paket utama | github.com/google/uuid |
Module publik dengan subdirektori driver/mysql | github.com/go-sql-driver/mysql |
Import path selalu absolut dari akar module; path relatif seperti ./penyapa tidak bisa dipakai. Go memilih model ini agar satu import path selalu merujuk package yang sama di mana pun dipakai.
Package main dan Package Library #
Go membedakan dua peran package. Package bernama main adalah program utuh: compiler menghasilkan berkas biner dengan titik masuk fungsi main(). Package dengan nama lain adalah library yang tidak bisa dieksekusi langsung, tetapi dipakai package lain. Konsekuensinya, package main sebaiknya dijaga tipis — cukup membaca argumen dan memanggil fungsi library.
Bundle chapter ini berisi module kecil src/ yang memperlihatkan kedua peran tersebut dalam satu struktur:
src/
├── go.mod ← module contoh.com/katalog
├── main.go ← package main (program)
└── penyapa/
└── penyapa.go ← package penyapa (library)Berkas src/main.go memanggil fungsi dari package penyapa:
package main
import (
"fmt"
"contoh.com/katalog/penyapa"
)
func main() {
fmt.Println(penyapa.Salam("Budi"))
penyapa.Tampilkan("Siti")
}Sementara src/penyapa/penyapa.go berisi dua fungsi library yang menerima dan mengembalikan string:
package penyapa
import "fmt"
// Salam mengembalikan sapaan utuh untuk nama yang diberikan.
func Salam(nama string) string {
return "Halo, " + nama + "!"
}
// Tampilkan mencetak sapaan ke standard output.
func Tampilkan(nama string) {
fmt.Println(Salam(nama))
}Karena go.mod sudah ada di dalam src/, kita menjalankan program dari dalam direktori tersebut:
cd src && go run .Output yang Dihasilkan:
Halo, Budi!
Halo, Siti!Mari kita telaah baris demi baris kedua berkas di atas:
main.gobaris 1 (package main): menandai berkas ini sebagai program yang dapat dieksekusi.main.gobaris 3-7 (blokimport): mengimpor standard libraryfmtdan package lokalcontoh.com/katalog/penyapadalam satu blok kurung.main.gobaris 10-11: memanggilSalamdanTampilkandari packagepenyapamelalui nama package sebagai prefiks.penyapa.gobaris 1 (package penyapa): menempatkan kedua fungsi ke packagepenyapa, bukanmain, sehingga tidak dapat dieksekusi langsung.penyapa.gobaris 6-8 (func Salam): fungsi publik yang hanya mengembalikan string tanpa efek samping — ciri library yang mudah diuji.
Import Cycle Ditolak Compiler #
Ketika package A mengimpor package B, sementara B juga mengimpor A, terbentuklah import cycle. Go menolak hubungan melingkar ini sejak kompilasi karena urutan inisialisasi kedua package tidak dapat ditentukan. Berikut hasil kompilasi nyata dua package yang saling mengimpor (demo dijalankan di direktori sementara):
package contoh.com/siklus
imports contoh.com/siklus/a from main.go
imports contoh.com/siklus/b from a.go
imports contoh.com/siklus/a from b.go: import cycle not allowedPesan tersebut menelusuri rantai impor dari titik masuk: main → a → b → kembali ke a. Solusinya: pindahkan tipe atau fungsi bersama ke package ketiga yang lebih rendah sehingga arah dependensi selalu satu arah.
Dengan memahami aturan direktori-package dan import path, fondasi kita siap membaca berkas go.mod secara tuntas — mulai dari go mod init hingga directive module, go, dan toolchain — pada chapter go.mod dan Module
.
Referensi #
- Spesifikasi Go: Import declarations — aturan resmi pernyataan import.
- Tutorial: Create a Go module — tutorial resmi module multi-package.
- Go Modules Reference — rujukan model module dan import path.
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
src/main.go
Unduh berkaspackage main
import (
"fmt"
"contoh.com/katalog/penyapa"
)
func main() {
fmt.Println(penyapa.Salam("Budi"))
penyapa.Tampilkan("Siti")
}
src/penyapa/penyapa.go
Unduh berkaspackage penyapa
import "fmt"
// Salam mengembalikan sapaan utuh untuk nama yang diberikan.
func Salam(nama string) string {
return "Halo, " + nama + "!"
}
// Tampilkan mencetak sapaan ke standard output.
func Tampilkan(nama string) {
fmt.Println(Salam(nama))
}