This guide is based on the official “Create a module” tutorial.

This guide introduces a few fundamental features of the Go language. If you’re just getting started with Go, be sure to take a look at the “Get Started with Go” guide, which introduces the go command, Go modules, and very simple Go code.

In this guide you’ll create two modules. The first is a library which is intended to be imported by other libraries or applications. The second is a caller application which will use the first.

This guide’s sequence includes six brief topics that each illustrate a different part of the language.

  1. Create a module – Write a small module with functions you can call from another module.
  2. Call your code from another module – Import and use your new module.
  3. Return and handle an error – Add simple error handling.
  4. Return a random greeting – Handle data in slices (Go’s dynamically-sized arrays).
  5. Return greetings for multiple people – Store key/value pairs in a map.
  6. Add a test – Use Go’s built-in unit testing features to test your code.
  7. Compile and install the application – Compile and install your code locally.

This guide requires you to push code to remote source code repositories. A unique user, {{{.GITEA_USERNAME}}}, has been automatically created for you, as have the repositories {{{.GREETINGS}}} and {{{.HELLO}}}. For more details on how play-with-go.dev guides work, please see the Introduction to play-with-go.dev guides guide.

Prerequisites

You should already have completed:

This guide is running using:

$ go version
go version go1.19.1 linux/amd64

Create a module that others can use

Start by creating a Go module. In a module, you collect one or more related packages for a discrete and useful set of functions. For example, you might create a module with packages that have functions for doing financial analysis so that others writing financial applications can use your work.

Go code is grouped into packages, and packages are grouped into modules. Your package’s module specifies the context Go needs to run the code, including the Go version the code is written for and the set of other modules it requires.

As you add or improve functionality in your module, you publish new versions of the module. Developers writing code that calls functions in your module can import the module’s updated packages and test with the new version before putting it into production use.

As with all play-with-go.dev guides, you start in your home directory:

$ pwd
/home/gopher

Create a greetings directory for your Go module source code. This is where you’ll write your module code:

$ mkdir /home/gopher/greetings
$ cd /home/gopher/greetings

Start your module using the go mod init command to create a go.mod file. In this guide you will publish your greetings module to {{{.GREETINGS}}}:

$ go mod init {{{.GREETINGS}}}
go: creating new go.mod: module {{{.GREETINGS}}}

The go mod init command creates a go.mod file that identifies your code as a module that might be used from other code. The file you just created includes only the name of your module and the Go version your code supports:

$ cat go.mod
module {{{.GREETINGS}}}

go 1.19

As you add dependencies – meaning packages from other modules – the go.mod file will list the specific module versions to use. This keeps builds reproducible and gives you direct control over which module versions to use.

Now let’s create greetings.go:

package greetings

import "fmt"

// Hello returns a greeting for the named person.
func Hello(name string) string {
	// Return a greeting that embeds the name in a message.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message
}

This is the first code for your module. It returns a greeting to any caller that asks for one. You’ll write code that calls this function in the next step.

In this code, you:

  • Declare a greetings package to collect related functions.
  • Implement a Hello function to return the greeting. This function takes a name parameter whose type is string, and returns a string. In Go, a function whose name starts with a capital letter can be called by a function not in the same package. This is known in Go as an exported name.
  • Declare a message variable to hold your greeting. In Go, the := operator is a shortcut for declaring and initializing a variable in one line (Go uses the value on the right to determine the variable’s type). Let’s rewrite this the long way:
package greetings

import "fmt"

// Hello returns a greeting for the named person.
func Hello(name string) string {
	// Return a greeting that embeds the name in a message.
	var message string
	message = fmt.Sprintf("Hi, %v. Welcome!", name)
	return message
}
  • Use the fmt package’s Sprintf function to create a greeting message. The first argument is a format string, and Sprintf substitutes the name parameter’s value for the %v format verb. Inserting the value of the name parameter completes the greeting text.
  • Return the formatted greeting text to the caller.

For people to be able to use your module you need to publish it. You publish a module by pushing a commit to a version control system like GitHub. You will publish your module to {{{.GREETINGS}}}.

Initialise a local git repository for your greetings module:

$ git init -q
$ git remote add origin https://{{{.GREETINGS}}}.git

Add and commit the greetings.go file you created earlier:

$ git add go.mod greetings.go
$ git commit -q -m 'Initial commit'

Publish this commit by pushing it to the remote repository:

$ git push -q origin main
remote: . Processing 1 references        
remote: Processed 1 references in total        

Call your code from another module

You’ll now write code that you can execute as an application, which makes calls to the Hello function in the greetings module you published to {{{.GREETINGS}}}.

Create the directory /home/gopher/hello for your Go module source code. This is where you’ll write your caller.

$ mkdir /home/gopher/hello
$ cd /home/gopher/hello

Create a new module for this hello package using the go mod init command to create a go.mod file as you did before, but this time using the unique path for the hello module:

$ go mod init {{{.HELLO}}}
go: creating new go.mod: module {{{.HELLO}}}

Declare a dependency on {{{.GREETINGS}}} using go get:

$ go get {{{.GREETINGS}}}
go: downloading {{{.GREETINGS}}} v0.0.0-20060102150405-abcedf12345
go: added {{{.GREETINGS}}} v0.0.0-20060102150405-abcedf12345

Without any version specified, go get will retrieve the latest version of the greetings module. And because you didn’t publish a specific version, go get resolves a pseudo version from the commit you pushed, specifically:

$ go list -m -f {{.Version}} {{{.GREETINGS}}}
v0.0.0-20060102150405-abcedf12345

Create hello.go as follows:

package main

import (
	"fmt"

	"{{{.GREETINGS}}}"
)

func main() {
	// Get a greeting message and print it.
	message := greetings.Hello("Gladys")
	fmt.Println(message)
}

In this code, you:

  • Declare a main package. In Go, code executed as an application must go in a main package.
  • Import two packages: {{{.GREETINGS}}} and fmt. This gives your code access to functions in those packages. Importing {{{.GREETINGS}}} (the package contained in the module you created earlier) gives you access to the Hello function. You also import fmt, with functions for handling input and output text (such as printing text to the console).
  • Get a greeting by calling the greetings package’s Hello function.

Build and run your program:

$ go build
$ ./hello
Hi, Gladys. Welcome!

Congrats! You’ve written two functioning modules. Next you’ll add some error handling.

Return and handle an error

Handling errors is an essential feature of solid code. In this section, you’ll add a bit of code to return an error from the greetings module, then handle it in the caller.

Return to the greetings module directory:

$ cd /home/gopher/greetings

There’s no sense sending a greeting back if you don’t know who to greet. Return an error to the caller if the name is empty. Update greetings.go as follows:

package greetings

import (
	"errors"
	"fmt"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
	// If no name was given, return an error with a message.
	if name == "" {
		return "", errors.New("empty name")
	}

	// If a name was received, return a value that embeds the name
	// in a greeting message.
	message := fmt.Sprintf("Hi, %v. Welcome!", name)
	return message, nil
}

In this code, you:

  • Change the function so that it returns two values: a string and an error. Your caller will check the second value to see if an error occurred. (Any Go function can return multiple values.)
  • Import the Go standard library errors package so you can use its errors.New function.
  • Add an if statement to check for an invalid request (an empty string where the name should be) and return an error if the request is invalid. The errors.New function returns an error with your message inside.
  • Add nil (meaning no error) as a second value in the successful return. That way, the caller can see that the function succeeded.

At this point your hello module is referring to a pseudo version that represents the previous commit you pushed. Therefore, you need to publish a new version of the greetings module.

Add and commit the changes we made to greetings.go:

$ git add greetings.go
$ git commit -q -m 'Added error handling'

Note the id of the commit we just created:

$ greetings_error_commit=$(git rev-parse HEAD)

Republish the greetings module:

$ git push -q origin main
remote: . Processing 1 references        
remote: Processed 1 references in total        

Return to the hello module directory to now use this new version:

$ cd /home/gopher/hello

By default, go get uses proxy.golang.org to resolve and download modules. In order to improve the proxy’s caching and serving latencies, new versions may not show up right away. Therefore, to be sure you do not resolve a stale latest version, use the latest commit of the greetings module as an explicit version:

$ go get {{{.GREETINGS}}}@$greetings_error_commit
go: downloading {{{.GREETINGS}}} v0.0.0-20060102150405-abcedf12345
go: upgraded {{{.GREETINGS}}} v0.0.0-20060102150405-abcedf12345 => v0.0.0-20060102150405-abcedf12345

In your hello.go, handle the error now returned by the Hello function, along with the non-error value:

package main

import (
	"fmt"
	"log"

	"{{{.GREETINGS}}}"
)

func main() {
	// Set properties of the predefined Logger, including
	// the log entry prefix and a flag to disable printing
	// the time, source file, and line number.
	log.SetPrefix("greetings: ")
	log.SetFlags(0)

	// Request a greeting message.
	message, err := greetings.Hello("")
	// If an error was returned, print it to the console and
	// exit the program.
	if err != nil {
		log.Fatal(err)
	}

	// If no error was returned, print the returned message
	// to the console.
	fmt.Println(message)
}

In this code, you:

  • Configure the log package to print the command name ("greetings: ") at the start of its log messages, without a time stamp or source file information.
  • Assign both of the Hello return values, including the error, to variables.
  • Change the Hello argument from Gladys’s name to an empty string, so you can try out your error-handling code.
  • Look for a non-nil error value. There’s no sense continuing in this case.
  • Use the functions in the standard library’s log package to output error information. If you get an error, you use the log package’s Fatal function to print the error and stop the program.

Run hello.go to confirm that the code works; now that you’re passing in an empty name, you’ll get an error:

$ go run hello.go
greetings: empty name
exit status 1

That’s essentially how error handling in Go works: Return an error as a value so the caller can check for it. It’s pretty simple.

Return a random greeting

Now you’ll change your code so that instead of returning the same greeting every time, it returns one of several predefined greeting messages.

To do this, you’ll use a Go slice. A slice is like an array, except that it’s dynamically sized as you add and remove items. It’s one of the most useful types in Go. You’ll add a small slice to contain three greeting messages, then have your code return one of the messages randomly.

Return to the greetings module:

$ cd /home/gopher/greetings

Update greetings.go as follows:

package greetings

import (
	"errors"
	"fmt"
	"math/rand"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
	// If no name was given, return an error with a message.
	if name == "" {
		return name, errors.New("empty name")
	}
	// Create a message using a random format.
	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}

// init sets initial values for variables used in the function.
func init() {
	// For truly random greetings, import "time" and replace the call
	// to rand.Seed with:
	//
	// rand.Seed(time.Now().UnixNano())
	//
	// Calling rand.Seed with a constant value means that we always
	// generate the same pseudo-random sequence.
	rand.Seed(1)
}

// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
	// A slice of message formats.
	formats := []string{
		"Hi, %v. Welcome!",
		"Great to see you, %v!",
		"Hail, %v! Well met!",
	}

	// Return one of the message formats selected at random.
	return formats[rand.Intn(len(formats))]
}

In this code, you:

  • Add a randomFormat function that returns a randomly selected format for a greeting message. Note that randomFormat starts with a lowercase letter, making it accessible only to code in its own package (in other words, it’s not exported).
  • In randomFormat, declare a formats slice with three message formats. When declaring a slice, you omit its size in the brackets, like this: []string. This tells Go that the array underlying a slice can be dynamically sized.
  • Use the math/rand package to generate a random number for selecting an item from the slice.
  • Add an init function to seed the rand package. Go executes init functions automatically at program startup, after global variables have been initialized.
  • In Hello, call the randomFormat function to get a format for the message you’ll return, then use the format and name value together to create the message.
  • Return the message (or an error) as you did before.

Note that you are seeding the math/rand package with a constant seed. This means the code will always generate the same pseudo-random sequence.

You now need to re-publish the updated greetings module so that we can use it in our hello module.

Add and commit the changes you just made:

$ git add greetings.go
$ git commit -q -m 'Added random format'

Note the id of the commit we just created:

$ greetings_random_commit=$(git rev-parse HEAD)

Republish the greetings module:

$ git push -q origin main
remote: . Processing 1 references        
remote: Processed 1 references in total        

Return to the hello module directory and use this new version:

$ cd /home/gopher/hello
$ go get {{{.GREETINGS}}}@$greetings_random_commit
go: downloading {{{.GREETINGS}}} v0.0.0-20060102150405-abcedf12345
go: upgraded {{{.GREETINGS}}} v0.0.0-20060102150405-abcedf12345 => v0.0.0-20060102150405-abcedf12345

Re-add Gladys’s name as an argument to the Hello function call in hello.go:

package main

import (
	"fmt"
	"log"

	"{{{.GREETINGS}}}"
)

func main() {
	// Set properties of the predefined Logger, including
	// the log entry prefix and a flag to disable printing
	// the time, source file, and line number.
	log.SetPrefix("greetings: ")
	log.SetFlags(0)

	// Request a greeting message.
	message, err := greetings.Hello("Gladys")
	// If an error was returned, print it to the console and
	// exit the program.
	if err != nil {
		log.Fatal(err)
	}

	// If no error was returned, print the returned message
	// to the console.
	fmt.Println(message)
}

Run hello.go to confirm the code works. Notice that because of the constant seed, running the program always gives the same greeting format. We will come back to that shortly:

$ go run hello.go
Hail, Gladys! Well met!
$ go run hello.go
Hail, Gladys! Well met!

That’s an introduction to a Go slice. To get even more use out of this type, you’ll use a slice to greet multiple people, where we will start to see some “real” pseudo-random greetings!

Return greetings for multiple people

In the last changes you’ll make to the greetings module code, you’ll add support for getting greetings for multiple people in one request. In other words, you’ll handle a multiple-value input and pair values with a multiple-value output.

Change back to the greetings module:

$ cd /home/gopher/greetings

To do this, you’ll need to pass a set of names to a function that can return a greeting for each of them. Changing the Hello function’s parameter from a single name to a set of names would change the function signature. Given that you have already published the greetings module, other users might already have written code calling Hello, that change would break their programs. In this situation, a better choice is to give new functionality a new name.

Add a new function Hellos that takes a set of names. For the sake of simplicity, have the new function call the existing one. Keeping both functions in the package leaves the original for existing callers (or future callers who only need one greeting) and adds a new one for callers that want the expanded functionality:

package greetings

import (
	"errors"
	"fmt"
	"math/rand"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
	// If no name was given, return an error with a message.
	if name == "" {
		return name, errors.New("empty name")
	}
	// Create a message using a random format.
	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}

// Hellos returns a map that associates each of the named people
// with a greeting message.
func Hellos(names []string) (map[string]string, error) {
	// A map to associate names with messages.
	messages := make(map[string]string)
	// Loop through the received slice of names, calling
	// the Hello function to get a message for each name.
	for _, name := range names {
		message, err := Hello(name)
		if err != nil {
			return nil, err
		}
		// In the map, associate the retrieved message with
		// the name.
		messages[name] = message
	}
	return messages, nil
}

// init sets initial values for variables used in the function.
func init() {
	// For truly random greetings, import "time" and replace the call
	// to rand.Seed with:
	//
	// rand.Seed(time.Now().UnixNano())
	//
	// Calling rand.Seed with a constant value means that we always
	// generate the same pseudo-random sequence.
	rand.Seed(1)
}

// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
	// A slice of message formats.
	formats := []string{
		"Hi, %v. Welcome!",
		"Great to see you, %v!",
		"Hail, %v! Well met!",
	}

	// Return one of the message formats selected at random.
	return formats[rand.Intn(len(formats))]
}

In this code, you:

  • Add a Hellos function whose parameter is a slice of names rather than a single name. Also, you change one of its return types from a string to a map so you can return names mapped to greeting messages.
  • Have the new Hellos function call the existing Hello function. This leaves both functions in place.
  • Create a messages map to associate each of the received names (as a key) with a generated message (as a value). In Go, you initialize a map with the following syntax: make(map[key-type]value-type). You have the Hello function return this map to the caller.
  • Loop through the names your function received, checking that each has a non-empty value, then associate a message with each. In this for loop, range returns two values: the index of the current item in the loop and a copy of the item’s value. You don’t need the index, so you use the Go blank identifier (an underscore) to ignore it.

Instead of republishing the greetings module, let’s add a replace directive in our hello module that tells the go command to use a local directory instead of a published module.

First return to the hello module:

$ cd /home/gopher/hello

Now add a replace directive using go mod edit:

$ go mod edit -replace {{{.GREETINGS}}}=/home/gopher/greetings

We can see the result in the hello module go.mod file:

$ cat go.mod
module {{{.HELLO}}}

go 1.19

require {{{.GREETINGS}}} v0.0.0-20060102150405-abcedf12345 // indirect

replace {{{.GREETINGS}}} => /home/gopher/greetings

In your hello.go calling code, pass a slice of names, then print the contents of the names/messages map you get back:

package main

import (
	"fmt"
	"log"

	"{{{.GREETINGS}}}"
)

func main() {
	// Set properties of the predefined Logger, including
	// the log entry prefix and a flag to disable printing
	// the time, source file, and line number.
	log.SetPrefix("greetings: ")
	log.SetFlags(0)

	// A slice of names.
	names := []string{"Gladys", "Samantha", "Darrin"}

	// Request greeting messages for the names.
	messages, err := greetings.Hellos(names)
	if err != nil {
		log.Fatal(err)
	}
	// If no error was returned, print the returned map of
	// messages to the console.
	fmt.Println(messages)
}

With these changes, you:

  • Create a names variable as a slice type holding three names.
  • Pass the names variable as the argument to the Hellos function.

Run hello.go to confirm that the code works, the output should be a string representation of the map associating names with messages:

$ go run hello.go
map[Darrin:Hail, Darrin! Well met! Gladys:Hail, Gladys! Well met! Samantha:Hi, Samantha. Welcome!]

This section introduced maps for representing name/value pairs. It also introduced the idea of preserving backward compatibility by implementing a new function for new or changed functionality in a module. In the next section, you’ll use built-in features to create a unit test for your code.

Add a test

Now that you’ve gotten your code to a stable place (nicely done, by the way), add a test. Testing your code during development can expose bugs that find their way in as you make changes. In this topic, you add a test for the Hello function.

Go’s built-in support for unit testing makes it easier to test as you go. Specifically, using naming conventions, Go’s testing package, and the go test command, you can quickly write and execute tests. You will create this test in the greetings module.

Return to the greetings module:

$ cd /home/gopher/greetings

Create a file called greetings_test.go:

package greetings

import (
	"regexp"
	"testing"
)

// TestHelloName calls greetings.Hello with a name, checking
// for a valid return value.
func TestHelloName(t *testing.T) {
	name := "Gladys"
	want := regexp.MustCompile(`\b` + name + `\b`)
	msg, err := Hello(name)
	if !want.MatchString(msg) || err != nil {
		t.Fatalf(`Hello("Gladys") = %q, %v, want match for %#q, <nil>`, msg, err, want)
	}
}

// TestHelloEmpty calls greetings.Hello with an empty string,
// checking for an error.
func TestHelloEmpty(t *testing.T) {
	msg, err := Hello("")
	if msg != "" || err == nil {
		t.Fatalf(`Hello("") = %q, %v, want "", error`, msg, err)
	}
}

If this code, you:

  • Implement test functions in the same package as the code you’re testing.
  • Create two test functions to test the greetings.Hello function. Test function names have the form TestName, where Name is specific to the test. Also, test functions take a pointer to the testing package’s testing.T as a parameter. You use this parameter’s methods for reporting and logging from your test.
  • Implement TestHelloName. TestHelloName calls the Hello function, passing a name value with which the function should be able to return a valid response message. If the call returns an error or an unexpected response message (one that doesn’t include the name you passed in), you use the t parameter’s Fatalf method to print a message to the console and end execution.
  • Implement TestHelloEmpty. TestHelloEmpty calls the Hello function with an empty string. This test is designed to confirm that your error handling works. If the call returns a non-empty string or no error, you use the t parameter’s Fatalf method to print a message to the console and end execution.

At the command line in the greetings directory, run the go test command to execute the test. The go test command executes test functions (whose names begin with Test) in test files (whose names end with _test.go). You can add the -v flag to get verbose output that lists all of the tests and their results.

The tests should pass:

$ go test
PASS
ok  	{{{.GREETINGS}}}	0.001s
$ go test -v
=== RUN   TestHelloName
--- PASS: TestHelloName (0.00s)
=== RUN   TestHelloEmpty
--- PASS: TestHelloEmpty (0.00s)
PASS
ok  	{{{.GREETINGS}}}	0.001s

You will now break the greetings.Hello function to view a failing test. The TestHelloName test function checks the return value for the name you specified as a Hello function parameter. To view a failing test result, change the greetings.Hello function so that it no longer includes the name:

package greetings

import (
	"errors"
	"fmt"
	"math/rand"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
	// If no name was given, return an error with a message.
	if name == "" {
		return name, errors.New("empty name")
	}
	// Create a message using a random format.
	// message := fmt.Sprintf(randomFormat(), name)
	message := fmt.Sprint(randomFormat())
	return message, nil
}

// Hellos returns a map that associates each of the named people
// with a greeting message.
func Hellos(names []string) (map[string]string, error) {
	// A map to associate names with messages.
	messages := make(map[string]string)
	// Loop through the received slice of names, calling
	// the Hello function to get a message for each name.
	for _, name := range names {
		message, err := Hello(name)
		if err != nil {
			return nil, err
		}
		// In the map, associate the retrieved message with
		// the name.
		messages[name] = message
	}
	return messages, nil
}

// init sets initial values for variables used in the function.
func init() {
	// For truly random greetings, import "time" and replace the call
	// to rand.Seed with:
	//
	// rand.Seed(time.Now().UnixNano())
	//
	// Calling rand.Seed with a constant value means that we always
	// generate the same pseudo-random sequence.
	rand.Seed(1)
}

// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
	// A slice of message formats.
	formats := []string{
		"Hi, %v. Welcome!",
		"Great to see you, %v!",
		"Hail, %v! Well met!",
	}

	// Return one of the message formats selected at random.
	return formats[rand.Intn(len(formats))]
}

Note that the highlighted lines change the value that the function returns, as if the name argument had been accidentally removed.

At the command line in the greetings directory, run go test to execute the test. This time, run go test without the -v flag. The output will include results for only the tests that failed, which can be useful when you have a lot of tests. The TestHelloName test should fail – TestHelloEmpty still passes:

$ go test
--- FAIL: TestHelloName (0.00s)
    greetings_test.go:15: Hello("Gladys") = "Hail, %v! Well met!", <nil>, want match for `\bGladys\b`, <nil>
FAIL
exit status 1
FAIL	{{{.GREETINGS}}}	0.001s

Let’s restore greetings.Hello to a working state

package greetings

import (
	"errors"
	"fmt"
	"math/rand"
)

// Hello returns a greeting for the named person.
func Hello(name string) (string, error) {
	// If no name was given, return an error with a message.
	if name == "" {
		return name, errors.New("empty name")
	}
	// Create a message using a random format.
	message := fmt.Sprintf(randomFormat(), name)
	return message, nil
}

// Hellos returns a map that associates each of the named people
// with a greeting message.
func Hellos(names []string) (map[string]string, error) {
	// A map to associate names with messages.
	messages := make(map[string]string)
	// Loop through the received slice of names, calling
	// the Hello function to get a message for each name.
	for _, name := range names {
		message, err := Hello(name)
		if err != nil {
			return nil, err
		}
		// In the map, associate the retrieved message with
		// the name.
		messages[name] = message
	}
	return messages, nil
}

// init sets initial values for variables used in the function.
func init() {
	// For truly random greetings, import "time" and replace the call
	// to rand.Seed with:
	//
	// rand.Seed(time.Now().UnixNano())
	//
	// Calling rand.Seed with a constant value means that we always
	// generate the same pseudo-random sequence.
	rand.Seed(1)
}

// randomFormat returns one of a set of greeting messages. The returned
// message is selected at random.
func randomFormat() string {
	// A slice of message formats.
	formats := []string{
		"Hi, %v. Welcome!",
		"Great to see you, %v!",
		"Hail, %v! Well met!",
	}

	// Return one of the message formats selected at random.
	return formats[rand.Intn(len(formats))]
}

And re-run go test to verify our change:

$ go test
PASS
ok  	{{{.GREETINGS}}}	0.001s

This section introduced Go’s built-in support for unit testing. In the next section, you’ll see how to compile and install your code to run it locally.

Compile and install the application

In the last section, you’ll learn a new go command. While the go run command is a useful shortcut for compiling and running a single-file program, it doesn’t generate a binary executable you can easily run again. If you want one of those, a good choice is to run the go install command, which compiles your code and installs the resulting binary executable where you can run it.

Change to the directory that contains the hello module:

$ cd /home/gopher/hello

Discover the Go install path, where the go command will install the current package:

$ go list -f '{{.Target}}'
/home/gopher/go/bin/hello

Add the Go install directory to your system’s shell path (this is generally a one-off step, you won’t need to do this for other programs). That way, you’ll be able to run your program’s executable without specifying where the executable is:

$ goinstalldir="$(dirname "$(go list -f '{{.Target}}')")"
$ export PATH="$goinstalldir:$PATH"

Once you’ve updated the shell path, run the go install command to compile and install the package:

$ go install

Run your application by simply typing its name:

$ hello
map[Darrin:Hail, Darrin! Well met! Gladys:Hail, Gladys! Well met! Samantha:Hi, Samantha. Welcome!]

Conclusion

That wraps up this guide!

As a next step you might like to consider: