TTuck

Go code is organized in packages, and packages in modules. A package is a directory of .go files that share a package clause. A module is a tree of packages with a go.mod file at its root, which names the module path and lists its dependencies. This repository is the module github.com/clivern/tuck.

package main

A package is imported by its full path: the module path plus the directory. The geometry package lives in the geometry/ folder next to this file. An alias, geo, is the name used in this file. A blank import runs init and gives no name to call.

import (
	"fmt"

	geo "github.com/clivern/tuck/90_packages_and_modules/geometry"
	_ "github.com/clivern/tuck/90_packages_and_modules/notice"
)

func main() {

Exported names are used through the name chosen at the import, here geo.

	c := geo.Circle{Radius: 2}
	fmt.Println(c.Area(), c.Diameter())

Package-level variables are initialized, and init functions run, before main. Steps lists them: geometry's own inits, in source order, then the package that was blank-imported.

	fmt.Println(geo.Default)
	fmt.Println("init:", geo.Steps)

Lower-case names are private to their package.

	// geometry.round(1.234)
	// error: name round not exported by package geometry

Third-party packages are added with go get, which records them in go.mod and go.sum, for example: go get golang.org/x/sync/errgroup. go mod tidy removes dependencies you no longer use.

	fmt.Println("see the geometry folder for the package source")
}
$ go run ./90_packages_and_modules
12.57 4.0cm
{1}
init: [geometry geometry again notice]
see the geometry folder for the package source
$ go list ./90_packages_and_modules/...
github.com/clivern/tuck/90_packages_and_modules
github.com/clivern/tuck/90_packages_and_modules/geometry
github.com/clivern/tuck/90_packages_and_modules/geometry/internal/units
github.com/clivern/tuck/90_packages_and_modules/notice
$ head -3 go.mod
module github.com/clivern/tuck

go 1.27.1

90_packages_and_modules/geometry/geometry.go

Package geometry is a tiny library used by the Packages and Modules example. The package name is the last element of its import path.

package geometry

import (
	"math"

	"github.com/clivern/tuck/90_packages_and_modules/geometry/internal/units"
)

Names starting with an upper case letter are exported: other packages can use them.

type Circle struct {
	Radius float64
}

Area is exported, so callers can use it.

func (c Circle) Area() float64 {
	return round(math.Pi * c.Radius * c.Radius)
}

Diameter returns the diameter formatted in centimeters.

func (c Circle) Diameter() string {
	return units.Centimeters(2 * c.Radius)
}

round is lower case, so it is private to this package.

func round(f float64) float64 {
	return math.Round(f*100) / 100
}

Default is set by init.

var Default Circle

Steps names each init function as it runs. Inits in one package run in source order, and a package finishes its inits before the package that imports it.

var Steps []string

func init() {
	Default = Circle{Radius: 1}
	Steps = append(Steps, "geometry")
}

func init() {
	Steps = append(Steps, "geometry again")
}

90_packages_and_modules/geometry/internal/units/units.go

Package units formats lengths. It lives under internal/, so only code inside the geometry directory tree can import it.

package units

import "fmt"

Centimeters formats a length in centimeters.

func Centimeters(v float64) string {
	return fmt.Sprintf("%.1fcm", v)
}

90_packages_and_modules/notice/notice.go

Package notice has no API. The packages example blank-imports it so init runs for its side effect.

package notice

import "github.com/clivern/tuck/90_packages_and_modules/geometry"

init runs because of that blank import. geometry is imported here, so its inits have already finished.

func init() {
	geometry.Steps = append(geometry.Steps, "notice")
}