25. Package dan modul
Semua program yang kamu tulis sejauh ini berada dalam satu file.
Program sungguhan tumbuh melewati itu. Go memecah program ke beberapa
file memakai package: package adalah folder berisi file
.go yang saling berkaitan, dan bisa menyediakan fungsi dan
variabel untuk dipakai package lain.
Bab ini juga menjawab dua pertanyaan lama: apa arti sebenarnya dari
package main, dan mengapa huruf kapital di awal punya arti
khusus.
Satu folder adalah satu package
Setiap file .go dimulai dengan baris
package, dan semua file dalam satu folder harus punya baris
yang sama. File-file itu lalu bertindak sebagai satu package: fungsi di
satu file bisa memanggil fungsi di file lain, tanpa import di
antaranya.
Ini adalah exercises/25/examples/01-one-package/. Isinya
tiga file. greet.go:
// Chapter 25 example: the second file of package main.
package main
import "fmt"
func hello(name string) {
fmt.Printf("Hello, %s!\n", name)
}
func bye(name string) {
fmt.Printf("Goodbye, %s.\n", name)
}main.go:
// Chapter 25 example: one package, split across two files.
//
// Run from this folder:
// cd exercises/25/examples/01-one-package
// go run .
package main
func main() {
hello("Keiko")
bye("Keiko")
}File ketiga, go.mod, dijelaskan di bawah. Ada tiga hal
yang perlu diperhatikan:
main.gomemanggilhellotanpa import. Keduanya ada di package yang sama.main.gotidak punyaimport "fmt", karena tidak memakaifmt. Import berlaku untuk setiap file, bukan untuk package.greet.gotidak mengerjakan apa-apa sendiri. File ini hanya mendefinisikan fungsi.
Sampai sekarang kamu menjalankan satu file setiap kali. Untuk
menjalankan seluruh package, masuk ke foldernya dan
pakai go run ., dengan titik yang berarti "package di
folder ini". Dari folder go-work-mu:
cd exercises/25/examples/01-one-package
go run .
Output:
Hello, Keiko!
Goodbye, Keiko.
Aturan dari Bab 06 untuk menjalankan semuanya dari
go-work tidak berlaku di sini. Untuk kembali ke
go-work sesudahnya, folder ini berada empat tingkat di
bawah:
cd ../../../..
go run main.go hanya mengompilasi file yang kamu
sebutkan, jadi tidak bisa melihat greet.go. Di dalam folder
itu, perintahnya berhenti dengan:
# command-line-arguments
.\main.go:10:2: undefined: hello
.\main.go:11:2: undefined: bye
Fungsinya ada, tetapi Go tidak diberi tahu tentang filenya. Pakai
go run ..
Arti sebenarnya dari
package main
Bab 07 mengatakan bahwa package main menandai file
sebagai program yang bisa dijalankan. Sekarang sisa kalimat itu masuk
akal. Go punya dua jenis package:
package mainmembuat program. Package ini harus punyafunc main(), dan dari situlah program mulai.Nama package lainnya membuat pustaka: kumpulan kode untuk di-import package lain.
fmtdanstringsadalah pustaka. Pustaka tidak punyafunc main, dan kamu tidak bisa menjalankannya. Kalau dicoba:package twopackages/greet is not a main package
Jadi kerangka yang selama ini kamu salin berkata: package ini adalah program, dan program mulai di sini.
Memakai package dengan
import
Untuk membuat pustaka sendiri, taruh di foldernya sendiri, dengan
nama package sendiri. Ini adalah
exercises/25/examples/02-two-packages/:
02-two-packages/
go.mod
main.go
greet/
greet.go
greet/greet.go:
// Chapter 25 example: package greet, in its own folder.
package greet
import "fmt"
func Hello(name string) {
fmt.Printf("Hello, %s!\n", name)
}
func Bye(name string) {
fmt.Printf("Goodbye, %s.\n", name)
}main.go:
// Chapter 25 example: a program that imports a package of your own.
//
// Run from this folder:
// cd exercises/25/examples/02-two-packages
// go run .
package main
import "twopackages/greet"
func main() {
greet.Hello("Keiko")
greet.Bye("Keiko")
}Jalankan dari foldernya:
cd exercises/25/examples/02-two-packages
go run .
Output:
Hello, Keiko!
Goodbye, Keiko.
import "twopackages/greet" melakukan dua hal:
- Mencari package-nya.
twopackagesadalah nama modul ini, yang ditetapkan olehgo.mod, dangreetadalah folder di dalamnya. - Memberimu nama
greet, sehingga kamu bisa menulisgreet.Hello, sama sepertiimport "fmt"memberimufmt.Println.
Nama yang dipakai berasal dari baris package, yang
menurut kebiasaan sama dengan nama foldernya. Itu juga sebabnya
import "math/rand" memberimu rand.Intn:
path-nya punya dua bagian, dan package-nya bernama
rand.
Sebuah package dikompilasi sekali, berapa pun banyaknya file yang
meng-import-nya. Kalau dua file sama-sama meng-import
greet, keduanya berbagi package yang sama, termasuk
variabel apa pun di dalamnya. Meng-import tidak menjalankan fungsi
package itu. Hanya memanggilnya yang menjalankan.
Import yang tidak dipakai menghentikan program, seperti yang
ditunjukkan Bab 10 untuk fmt:
# twopackages
.\main.go:9:8: "twopackages/greet" imported and not used
Mengapa huruf kapital punya arti khusus
Bab 05 dan 11 memintamu memulai nama dengan huruf kecil, dan menjanjikan penjelasannya. Inilah penjelasannya. Nama yang dimulai dengan huruf kapital diekspor: package lain bisa memakainya. Nama yang dimulai dengan huruf kecil bersifat pribadi bagi package-nya.
Itu sebabnya kamu selalu menulis fmt.Println dengan
P kapital, dan mengapa greet.go di atas
memakai Hello, bukan hello. Itu juga sebabnya
kedua fungsi di 01-one-package boleh memakai huruf kecil:
keduanya hanya dipakai di dalam package-nya sendiri.
Ini aturan bahasa, bukan sekadar kebiasaan. Kalau
main.go memanggil greet.hello, Go
berhenti:
# twopackages
.\main.go:12:8: undefined: greet.hello
Di dalam package main, tidak ada yang meng-import
namamu, jadi huruf kecil adalah pilihan yang tepat. Itu sebabnya buku
ini memakainya sejak awal. Huruf pertama juga berlaku untuk variabel dan
konstanta di tingkat atas package, seperti yang akan kamu lihat di
mini-proyek.
Nama pendek dengan alias
import
Tidak ada cara untuk meng-import satu fungsi saja dari sebuah package. Setiap pemakaian punya nama package di depannya, dan itu memberi tahu pembaca asalnya. Tetapi kamu bisa memberi package nama yang lebih pendek untuk satu file, dengan menuliskannya sebelum path:
import (
"fmt"
mh "mathmodule/mathhelpers"
)
func main() {
fmt.Println(mh.Double(7))
fmt.Println(mh.Triple(7))
}Untuk package kecil buatanmu, alias bersifat opsional. Nama package
itu pendek, huruf kecil, dan satu kata, tanpa _. Jadi
package berisi pembantu matematika bernama mathhelpers,
bukan math_helpers.
Modul dan go.mod
Modul adalah folder berisi package dengan file
go.mod di puncaknya. File ini memberi nama modul, dan nama
itu menjadi awal setiap path import di dalamnya. Ini adalah
exercises/25/examples/02-two-packages/go.mod:
module twopackages
go 1.22
Baris terakhir menyatakan untuk versi Go mana kode ini ditulis.
Satu file yang dijalankan dengan go run <file>
tidak butuh modul, seperti kata Bab 06, karena hanya meng-import pustaka
standar. Begitu kamu meng-import package buatanmu sendiri, Go butuh
go.mod untuk mengetahui arti path itu. Folder-folder di bab
ini sudah punya. Untuk memulai programmu sendiri, buat folder, masuk ke
dalamnya, dan jalankan go mod init dengan nama
pilihanmu:
mkdir mygame
cd mygame
go mod init mygame
Perintah itu mencetak:
go: creating new go.mod: module mygame
go.mod yang ditulisnya memuat versi yang terpasang di
komputermu pada baris terakhir. Kamu hanya melakukan ini sekali untuk
setiap program.
Tempat menjalankan perintah itu penting:
go run .harus berjalan di folder yang berisipackage main. Di folder yang tidak punyago.moddi atasnya, perintah ini berhenti dengan:go: go.mod file not found in current directory or any parent directory; see 'go help modules'Menjalankan
go run exercises/25/examples/02-two-packages/main.godarigo-workjuga gagal. Go mencarigo.moddi folder tempat kamu berada, tidak menemukannya, dan berhenti dengan error yang memuatpackage twopackages/greet is not in std. Jadicddulu.go vet ./...memeriksa setiap package dalam modul, kalau kamu menjalankannya dari folder puncak modul. Perintah ini tidak mencetak apa-apa kalau tidak menemukan masalah.
Pembantu pribadi di dalam package
Fungsi yang tidak boleh dipanggil package lain cukup diberi nama
huruf kecil. Ini package di
exercises/25/examples/03-private-helper/mathhelpers/mathhelpers.go:
// Chapter 25 example: package mathhelpers.
package mathhelpers
func clamp(x int, lo int, hi int) int {
if x < lo {
return lo
}
if x > hi {
return hi
}
return x
}
func Double(x int) int {
return clamp(x*2, -100, 100)
}Double untuk semua orang, dan clamp adalah
pembantu khusus package ini. File dalam package yang sama bisa
memanggilnya, dan package lain tidak bisa. Go menegakkan aturan ini:
memanggil mathhelpers.clamp dari main.go
menghentikan program:
# privatehelper
.\main.go:20:26: clamp not exported by package mathhelpers
Dengan menjaga pembantu tetap pribadi, kamu bisa mengubahnya nanti tanpa merusak package lain.
Masuk ke exercises/25/examples/03-private-helper dan
jalankan go run ., yang mencetak 14 dan
100. Lalu hapus tanda komentar pada baris terakhir
main.go dan jalankan lagi untuk melihat error di atas.
PR
PR Bagian 5 punya modul sendiri untuk setiap soal:
go.mod, main.go yang sudah jadi, dan package
dalam folder yang harus kamu lengkapi. Buka terminal, cd ke
folder soalnya, lalu jalankan go run .. Setiap soal bisa
langsung dijalankan, tetapi mencetak teks pengganti atau jawaban yang
salah sampai kamu menyelesaikan package-nya. Jalankan juga
go vet ./... di sana.
Soal 1 — Package greet
Folder: exercises/25/homework/01-greet-module/.
Selesaikan greet/greet.go supaya
Hello(name) mencetak Hello, <name>! dan
Bye(name) mencetak Goodbye, <name>..
File main.go sudah jadi dan sudah memanggil kedua fungsi
itu.
Soal 2 — Pembantu matematika
Folder: exercises/25/homework/02-math-helpers/.
Selesaikan package di mathhelpers/mathhelpers.go:
Double(x)mengembalikanx * 2.Triple(x)mengembalikanx * 3.
main.go meng-import-nya dengan alias mh,
memanggil keduanya, dan mencetak hasilnya.
Soal 3 — Penghitung
Folder: exercises/25/homework/03-counter/.
Selesaikan package di counter/counter.go. Package ini
punya tiga fungsi:
Increment()menambah penghitung sebesar 1.Get()mengembalikan nilai saat ini.Reset()mengembalikan penghitung ke 0.
Penghitungnya sendiri adalah variabel di bagian atas file, di luar fungsi. Nilainya harus bertahan antarpanggilan (ingat kembali bagian cakupan di Bab 21). Beri nama huruf kecil, supaya hanya ketiga fungsi itu yang bisa menyentuhnya.
Tantangan — Alat string
Folder: exercises/25/homework/04-string-utils/.
Selesaikan package di stringutils/stringutils.go dengan
setidaknya tiga fungsi. Saran:
Shout(s)mengembalikansdalam huruf besar dengan tambahan!.Echo(s, n)mengembalikansyang diulangnkali dengan spasi di antaranya.ReverseWords(s)mengembalikan kata-katasdalam urutan terbalik.
Yang terakhir cukup menantang: pecah string menjadi kata, lalu balik
slice-nya. Kalau terlalu berat, ganti dengan fungsi lain buatanmu
sendiri, dan ubah main.go agar sesuai.
Stuck atau sudah selesai? Buka halaman solusi PR.