Skip to content

Packages, Modules and Workspaces

Packages

Every .go file begins with the package command, and the package's name should match the directory in which the file resides. The exceptions to this rule are package main and package test which serve as the entry points for applications and tests, respectively. Packages within the same directory should share the same package name. Anything within the same package can be accessed and utilized.

Simple package example

run command
go run src/packages/*
src/packages/nice_func.go
package main

func nice_func() {
    println("   I'm on the same directory than main, so my package has to be main")
}
src/packages/main.go
package main

func main() {
    println("Starting script")
    println("Now, calling nice_func that is defined from the same file directory, on nice_func.go file")
    nice_func()
}
output
Starting script
Now, calling nice_func that is defined from the same file directory, on nice_func.go file
    I'm on the same directory than main, so my package has to be main

Installing Packages

See Go Get and Go Mod TIDY.

Modules

In Go, a module is a collection of related Go packages that are versioned together as a single unit. Modules are the key component of the Go module system, introduced to manage package dependencies and versioning.

A Go module typically consists of one or more Go packages, along with a go.mod file that defines the module's requirements and dependencies. The go.mod file keeps track of the module's dependencies, versions, and the other modules it requires.

To create a new module in Go, you can use the go mod init command followed by the name of the module.

Public and Private Access

In Go, the concept of public and private access is determined by the first letter's case of a variable or function name. If the first letter of an identifier is in uppercase, it is exported and can be accessed from other packages. On the other hand, if the first letter is lowercase, the identifier is considered unexported and can only be accessed within the same package.

This convention helps maintain encapsulation and ensures that package-level variables and functions are used only where intended.

Example

run command
cd src/modules/
go run main.go
src/modules/mathpackage/mathpackage.go
package mathpackage

import "fmt"

func sum[T int | float64](a, b T) T {
    fmt.Println("   Accessing sum function. Private to mathpackage")
    return a + b
}

func SumInt(a, b int) int {
    return sum(a, b)
}

func SumFloat(a, b float64) float64 {
    return sum(a, b)
}
src/modules/car_package/car_package.go
package carpackage

import (
    "time"
)

type Car struct {
    Name     string
    NickName string
    Year     int
}

type fullCarPrivate struct {
    FullName string
    Year     int
    YearOld  int
}

func (c Car) CreateFullCar() fullCarPrivate {
    yearNow := time.Now().Year()
    finalCar := fullCarPrivate{
        FullName: c.Name + " " + c.NickName,
        Year:     c.Year,
        YearOld:  yearNow - c.Year,
    }
    return finalCar
}
src/modules/main.go
package main

import (
    "fmt"
    carpackage "start/modules/car_package"
    "start/modules/mathpackage"
)

func main() {
    intSum := mathpackage.SumInt(50, 21)
    fmt.Println("Result of SumInt: ", intSum)
    floatSum := mathpackage.SumFloat(3.14, 17.8)
    fmt.Println("Result of SumFloat: ", floatSum)

    fmt.Println("****")
    fmt.Println("Starting explore Car Module")
    car := carpackage.Car{
        Name:     "Corolla",
        NickName: "Giant car",
        Year:     2020,
    }
    fullCar := car.CreateFullCar()
    fmt.Println("Full Car name: ", fullCar.FullName)
    fmt.Println("Full Car Years Old: ", fullCar.YearOld)
}
output
    Accessing sum function. Private to mathpackage
Result of SumInt:  71
    Accessing sum function. Private to mathpackage
Result of SumFloat:  20.94
****
Starting explore Car Module
Full Car name:  Corolla Giant car
Full Car Years Old:  3

Workspaces

Introduced in Go 1.18, Go Workspaces allow developers to work on multiple modules simultaneously without having to edit go.mod files for each module. Each module within a workspace is treated as a main module when resolving dependencies.

To create a workspace, is necessary to execute go work init command. This will create a go.work file. The go.work file has use and replace directives that override the individual go.mod files, so there is no need to edit each go.mod file individually. The use directive adds a module on disk to the set of main modules in a workspace, while the replace directive replaces the contents of a specific version of a module, or all versions of a module, with contents found elsewhere.

Workspaces are flexible and support a variety of workflows. For example, you can add a feature to an upstream module and use it in your own module, work with multiple interdependent modules in the same repository, or switch between different dependency configurations.

References