In Go, packages provide a powerful way to organize and structure your code. A key feature is that files within the same package have direct access to each other’s variables, functions, and types. This promotes seamless collaboration between different parts of your codebase. Let’s explore how this works in practice.
If two files share the same package name within the same directory, they can access each other’s names, such as variables and functions. We can execute package using the following command:
go run *.go
All files within a directory sharing the same package name effectively share a namespace.
├── calculate
│ ├── solution_runner.go
│ └── types.go
In this example, importing the calculate package allows us to use all the names defined in both solution_runner.go and types.go.
Let’s assume we have the following folders structure. Here calculate and eiler folders are go packages.
├── calculate
│ ├── solution_runner.go
│ └── types.go
├── eiler
│ ├── eiler_10
│ │ └── eiler_10.go
│ ├── eiler_6
│ │ └── eiler_6.go
│ ├── eiler_7
│ │ └── eiler_7.go
│ ├── eiler_8
│ │ └── eiler_8.go
│ └── eiler_9
│ └── eiler_9.go
└── go.mod
To import names from the eiler package into solution_runner, we need to create a go.mod file for our project.
go mod init my-package-name
After running this command inside of folder we can see go.mod file
|
|
All import paths should be relative to this path specified in go.mod.
File: calculate/solution_runner.go
|
|
The location of the main file importing these modules doesn’t matter; only the path to the imported directory is important. For instance, if we have a package named eiler_10, all files within that directory can share variables and object names.
File: eiler/eiler_6/eiler_6.go
|
|
File: eiler/eiler_6/types_eiler_6.go
|
|
The Element type, defined in types_eiler_6.go, is used within eiler_6.go. When we import the eiler_6 package into our main function, we don’t encounter an error about Element being undefined. This is because it’s imported as part of the package.
We can also run everything as a package. If our main package has types defined in another file within that package, we can execute it like so:
go run .
go run calculate // if inside of parent directory
go run main.go
If we attempt to run only one file from the package, we’ll get an error indicating that an imported name is undefined.
Let’s create a module:
mkdir mymodule
cd mymodule
Inside mymodule, let’s create a go.mod file to mark this directory as a module for Go.
go mod init mymodule
Within go.mod:
|
|
mymodule is an alias we can use when importing this module.
Let’s create inside of mymodule a package named calc to define some functionality.
mkdir calc
cd calc
touch math.go
Inside math.go:
|
|
To test this module, let’s create a main module alongside mymodule.
cd ../..
mkdir main
cd main
go mod init main
touch main.go
After all we’ll have the following folders structure:
├── main
│ ├── go.mod
│ ├── main.go
│ └── utils.go
└── mymodule
├── calc
│ └── math.go
└── go.mod
Inside main.go:
|
|
Inside of main directory
go run .
If we try to run this, we’ll get an error:
package mymodule/calc is not in GOROOT (/usr/local/go/src/mymodule/calc)
This error occurs because we haven’t specified where Go should find the mymodule module within main/go.mod. Go attempted to search in GOROOT but couldn’t find it and, since the module name doesn’t resemble a URL, emitted this error.
We need to explicitly tell the Go compiler where mymodule is located. If the module is only on our local machine, we use the replace directive.
|
|
Let’s test again:
Inside the main directory
go run .
We’ll get almost equivalent error, but now we just need to get the module:
package mymodule/calc is not in GOROOT (/usr/local/go/src/mymodule/cal)
module mymodule provides package mymodule/calc and is replaced but not required; to add it:
go get mymodule
Let’s run the suggested command:
go get mymodule
Inside main/go.mod, we now have a new line:
require mymodule v0.0.0-00010101000000-000000000000 // indirect
This indicates the specific version of the mymodule module that the Go compiler will use for building.
Running main.go one more time should now work successfully!