How to Write Go Code

Introduction

This document demonstrates the development of a simple Go package inside a module and introduces the go tool, the standard way to fetch, build, and install Go modules, packages, and commands.

Note: This document assumes that you are using Go 1.13 or later and the GO111MODULE environment variable is not set. If you are looking for the older, pre-modules version of this document, it is archived here.

Code organization

Go programs are organized into packages. A package is a collection of source files in the same directory that are compiled together. Functions, types, variables, and constants defined in one source file are visible to all other source files within the same package.

A repository contains one or more modules. A module is a collection of related Go packages that are released together. A Go repository typically contains only one module, located at the root of the repository. A file named go.mod there declares the module path: the import path prefix for all packages within the module. The module contains the packages in the directory containing its go.mod file as well as subdirectories of that directory, up to the next subdirectory containing another go.mod file (if any).

Note that you don't need to publish your code to a remote repository before you can build it. A module can be defined locally without belonging to a repository. However, it's a good habit to organize your code as if you will publish it someday.

Each module's path not only serves as an import path prefix for its packages, but also indicates where the go command should look to download it. For example, in order to download the module golang.org/x/tools, the go command would consult the repository indicated by https://golang.org/x/tools (described more here).

An import path is a string used to import a package. A package's import path is its module path joined with its subdirectory within the module. For example, the module github.com/google/go-cmp contains a package in the directory cmp/. That package's import path is github.com/google/go-cmp/cmp. Packages in the standard library do not have a module path prefix.

Your first program

To compile and run a simple program, first choose a module path (we'll use github.com/user/hello) and create a go.mod file that declares it:

$ mkdir hello # Alternatively, clone it if it already exists in version control.
$ cd hello
$ go mod init github.com/user/hello
go: creating new go.mod: module github.com/user/hello
$ cat go.mod
module github.com/user/hello

go 1.13
$

The first statement in a Go source file must be package name. Executable commands must always use package main.

Next, create a file named hello.go inside that directory containing the following Go code:

package main

import "fmt"

func main() {
	fmt.Println("Hello, world.")
}

Now you can build and install that program with the go tool:

$ go install github.com/user/hello
$

This command builds the hello command, producing an executable binary. It then installs that binary as $HOME/go/bin/hello (or, under Windows, %USERPROFILE%\go\bin\hello.exe). You can change the install directory by setting the GOBIN environment variable.

$ go env -w GOBIN=/somewhere/else/bin
$

Commands like go install apply within the context of the module containing the current working directory. If the working directory is not within the github.com/user/hello module, go install may fail.

For convenience, go commands accept paths relative to the working directory, and default to the package in the current working directory if no other path is given. So in our working directory, the following commands are all equivalent:

$ go install github.com/user/hello
$ go install .
$ go install

Next, let's run the program to ensure it works. For added convenience, we'll add the install directory to our PATH to make running binaries easy:

# Windows users should consult https://github.com/golang/go/wiki/SettingGOPATH
# for setting %PATH%.
$ export PATH=$PATH:$(go env GOBIN)
$ hello
Hello, world.
$

If you're using a source control system, now would be a good time to initialize a repository, add the files, and commit your first change. Again, this step is optional: you do not need to use source control to write Go code.

$ git init
Initialized empty Git repository in /home/user/hello/.git/
$ git add go.mod hello.go
$ git commit -m "initial commit"
[master (root-commit) 0b4507d] initial commit
 1 file changed, 7 insertion(+)
 create mode 100644 go.mod hello.go
$

Importing packages from your module

Let's write a morestrings package and use it from the hello program. First, create a directory for the package named $HOME/hello/morestrings, and then a file named reverse.go in that directory with the following contents:

// Package morestrings implements additional functions to manipulate UTF-8
// encoded strings, beyond what is provided in the standard "strings" package.
package morestrings

// ReverseRunes returns its argument string reversed rune-wise left to right.
func ReverseRunes(s string) string {
	r := []rune(s)
	for i, j := 0, len(r)-1; i < len(r)/2; i, j = i+1, j-1 {
		r[i], r[j] = r[j], r[i]
	}
	return string(r)
}

Because our ReverseRunes function begins with an upper-case letter, it is exported, and can be used in other packages that import our morestrings package.

Let's test that the package compiles with go build:

$ cd $HOME/hello/morestrings
$ go build
$

This won't produce an output file. Instead it saves the compiled package in the local build cache.

After confirming that the morestrings package builds, let's use it from the hello program. To do so, modify your original $HOME/hello/hello.go to use the morestrings package:

package main

import (
	"fmt"

	"github.com/user/hello/morestrings"
)

func main() {
	fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
}

Install the hello program:

$ go install github.com/user/hello

Running the new version of the program, you should see a new, reversed message:

$ hello
Hello, Go!

Importing packages from remote modules

An import path can describe how to obtain the package source code using a revision control system such as Git or Mercurial. The go tool uses this property to automatically fetch packages from remote repositories. For instance, to use github.com/google/go-cmp/cmp in your program:

package main

import (
	"fmt"

	"github.com/user/hello/morestrings"
	"github.com/google/go-cmp/cmp"
)

func main() {
	fmt.Println(morestrings.ReverseRunes("!oG ,olleH"))
	fmt.Println(cmp.Diff("Hello World", "Hello Go"))
}

When you run commands like go install, go build, or go run, the remote module will automatically be added to your go.mod file (and downloaded):

$ go install github.com/user/hello
$ hello
Hello, Go!
  string(
- 	"Hello World",
+ 	"Hello Go",
  )
$ cat go.mod
module github.com/user/hello

go 1.13

require github.com/google/go-cmp v0.3.1
$

A module's path doesn't have to match the URL for its repository, but this convention is the easiest way to make your Go packages available for others to use. For more information on using remote modules with the go tool, see go help importpath and Using Go Modules.

Testing

Go has a lightweight test framework composed of the go test command and the testing package.

You write a test by creating a file with a name ending in _test.go that contains functions named TestXXX with signature func (t *testing.T). The test framework runs each such function; if the function calls a failure function such as t.Error or t.Fail, the test is considered to have failed.

Add a test to the morestrings package by creating the file $HOME/hello/morestrings/reverse_test.go containing the following Go code.

package morestrings

import "testing"

func TestReverseRunes(t *testing.T) {
	cases := []struct {
		in, want string
	}{
		{"Hello, world", "dlrow ,olleH"},
		{"Hello, 世界", "界世 ,olleH"},
		{"", ""},
	}
	for _, c := range cases {
		got := ReverseRunes(c.in)
		if got != c.want {
			t.Errorf("ReverseRunes(%q) == %q, want %q", c.in, got, c.want)
		}
	}
}

Then run the test with go test:

$ go test
PASS
ok  	github.com/user/morestrings 0.165s
$

Run go help test and see the testing package documentation for more detail.

What's next

Subscribe to the golang-announce mailing list to be notified when a new stable version of Go is released.

See Effective Go for tips on writing clear, idiomatic Go code.

Take A Tour of Go to learn the language proper.

Visit the documentation page for a set of in-depth articles about the Go language and its libraries and tools.

Getting help

For real-time help, ask the helpful gophers in the community-run gophers Slack server (grab an invite here).

The official mailing list for discussion of the Go language is Go Nuts.

Report bugs using the Go issue tracker.