Packages and visibility
Every Go file starts with a package clause. A package is all the .go files in one directory that share that clause: they compile together, and anything declared at the top level of one file is visible in all the others without an import. package main is special — it builds an executable, and its main function is where the program starts.
Go has no public, private or protected keywords. Visibility is decided by the first letter of the name: Discount, User and User.Name start with a capital, so they are exported and other packages can use them. percentOf and password are unexported — visible only inside their own package. The rule applies to functions, types, variables, constants, struct fields and methods alike.
package main
import (
"encoding/json"
"fmt"
)
// User is exported (capital U); so are Name and Email.
// password starts with a lower-case letter, so it is unexported:
// other packages, including encoding/json, cannot see it.
type User struct {
Name string
Email string
password string
}
func newUser(name, email, pw string) User { // unexported helper
return User{Name: name, Email: email, password: pw}
}
func main() {
u := newUser("ana", "[email protected]", "hunter2")
fmt.Println(u.password) // fine: same package
data, _ := json.Marshal(u)
fmt.Println(string(data)) // password is missing
}hunter2
{"Name":"ana","Email":"[email protected]"}encoding/json is a different package, so it cannot read unexported fields — a common surprise when an API response comes back missing data.
Add a field age int and set it in newUser. Does it appear in the JSON? Rename it to Age and try again.
// Package pricing computes prices. It lives under internal/,
// so only code inside example.com/shop can import it.
package pricing
// Discount is exported: other packages can call it.
func Discount(cents, percent int) int {
return cents - percentOf(cents, percent)
}
// percentOf is unexported: only code in package pricing can call it.
func percentOf(cents, percent int) int {
return cents * percent / 100
}One exported function, one unexported helper. Code outside package pricing can call pricing.Discount but not pricing.percentOf.
name percentOf not exported by package pricing
// main.go — in the same module as internal/pricing/pricing.go above
package main
import (
"fmt"
"example.com/shop/internal/pricing"
)
func main() {
fmt.Println(pricing.percentOf(2000, 25))
}$ go run .
# example.com/shop
./main.go:10:22: name percentOf not exported by package pricingpercentOf starts with a lower-case letter, so it belongs to package pricing alone. The compiler will not let another package reach it, even inside the same module.
Call the exported API (pricing.Discount). If other packages really need the helper, rename it with a capital letter and document it — exporting is a promise to keep it working.
package main
import (
"fmt"
"example.com/shop/internal/pricing"
)
func main() {
fmt.Println(pricing.Discount(2000, 25)) // 1500
}// Discount returns ...), is its documentation. go doc, editors and pkg.go.dev all show it. Write one for every exported identifier.