Utilizing Go Packages

Learn about packages, how to import them, and their usage in Go.

Go provides reusable blocks of code that can be imported into other code using packages. Packages in Go are synonymous with libraries or modules in other languages. Packages are the building blocks of Go programs that divide the content into understandable parts.

This lesson will cover how to declare and import a package. We’ll discuss how to deal with package name conflicts, explore rules around packages, and we’ll write our first main package.

Declaring a package

Go divides programs into packages, sometimes called modules or libraries in other languages. Packages live on a path, and the path is made to look like a path to a directory on a Unix-like filesystem.

All Go files in a directory must belong to the same package. The package is most commonly named the same as the directory it lives in. Declaring a package happens at the top of the file and should only be preceded by a comment. Declaring a package is as simple as the following:

Press + to interact
// Package main is the entrance point for our binary.
// The double slashes provides a comment until the end of the line.
/*
This is a comment that lasts until the closing star slash.
*/
package main

The package main is special. All other package names declare a package that must be imported into another package to be used. The package main will declare func main(), which is the starting point for a binary to run.

All Go files in a directory must have the same package header (compiler-enforced). These files, for most practical purposes, act as if they are concatenated together. Let's say we have a directory structure as follows:

mypackage/
file1.go
file2.go
Sample directory structure in Go

Then, file1.go and file2.go should have the following:

Press + to interact
package mypackage

When mypackage is imported by another package, it will include everything declared in all files in the mypackage directory.

Importing a package

There are two general types of packages:

  • The standard library (stdlib) packages

  • All other packages

Standard library packages stand out because they don't list some repository information in their path, such as the following:

Press + to interact
"fmt"
"encoding/json"
"archive/zip"

All other packages generally have repository information preceding them, as follows:

Press + to interact
"github.com/johnsiilver/golib/lru"
"github.com/kylelemons/godebug/pretty"

Note: A complete listing of stdlib packages can be found here.

To import packages, we use the import keyword. So, let's import the standard library fmt package and the mypackage package, which lives at github.com/devopsforgo/mypackage:

Press + to interact
package main
import (
"fmt"
"github.com/devopsforgo/mypackage"
)

The filenames are not part of the package path but simply the directory path.

Using a package

Once we've imported a package, we can start accessing functions, types, or variables declared in the package by prefacing what we want to access with the package's name and a period. For example, the fmt package has a function called Println() that can be used to print a line to stdout. If we want to use it, it is as simple as the following:

Press + to interact
func main(){
fmt.Println("Hello!")
}

Package name conflicts

Let's say we have two packages named mypackage. They both have the same name, so our program won't be able to tell which one we are referring to. We can rename a package import into whatever name we want:

Press + to interact
import(
"github.com/devopsforgo/mypackage"
jpackage "github.com/johnsiilver/mypackage"
)

The jpackage declares that in this package, we'll refer to github.com/johnsiilver/mypackage as jpackage. This ability allows us to use two similarly named packages as follows:

Press + to interact
mypackage.Print()
jpackage.Send()

Now, we'll look at an important rule around packages that improve compile-time and binary size.

Packages must be used

Let's introduce the following rule: If we import a package, we must use it.

One of the things that the Go authors noticed about many of the other programming languages being used at Google was that they often had unused imports.

This led to compile times that were longer than needed and, in some cases, binary sizes that were much bigger than required. Python files were packaged in a proprietary format to ship around production, and some of these unused imports were adding hundreds of megabytes to the files.

To prevent these types of problems, Go will not compile a program that imports a package but doesn't use it, as shown below:

Press + to interact
package main
import (
"fmt"
"sync"
)
func main() {
fmt.Println("Hello, playground")
}

In certain rare circumstances, we may need to do a side effects import, in which just loading the package causes something to happen, but we don't use the package. This should always be done in package main and requires prepending with an underscore (_):

Press + to interact
package main
import (
"fmt"
_ "sync" //Just an example
)
func main() {
fmt.Println("Hello, playground")
}

Next, we will declare a main package and discuss the basics of writing a Go program that imports a package.

A Go hello world

Let's write a simple Hello World! program. This example will demonstrate the following:

  • Declaring a package

  • Importing the fmt package from the standard library, which can print to our screen

  • Declaring the main() function of a program

  • Declaring a string variable using the := operator

  • Printing the variable to the screen

Let's see what this looks like:

Press + to interact
package main
import "fmt"
func main() {
hello:= "Hello World!"
fmt.Println(hello)
}

Let’s have a look at the highlighted lines shown in the code widget above:

  • Line 1: We declare the name of our package using the package keyword. The entrance point for any Go binary is a package named main that has a function called main().

  • Line 2: We import the fmt package. fmt has functions for doing string formatting and writing to various outputs.

  • Line 4: We declare a function called main that takes no arguments and returns no values. The main() is special, as when a binary is run, it starts by running the main() function. Go uses {} to show where a function starts and where a function ends (similar to C).

  • Line 5: We declare a variable named hello using the := operator. The := operator indicates that we wish to create a new variable and assign it a value in a single line. This is the most common, but not the only, way to declare a variable.

Note: Since Go is typed, := will assign the type based on the value. In this case, it will be a string, but if the value was an integer (such as 3), it would be the int type, and if a floating-point (such as 2.4), it would be the float64 type. If we wanted to declare a specific type, such as int8 or float32, we would need some modifications (which we'll talk about later).

  • Line 6: We call a function that is in the fmt package called Println. This function will print the contents of the hello variable to stdout followed by a new line character (\n).

Note that the way to use a function declared in another package is to use the package name (without quotes) + a period + the function's name. In this case, fmt.Println().

In this lesson, we have learned how to declare a package, import a package, what the function of the main package is, and how to write a basic Go program with a variable declaration.