Chapter 1. Getting Started Recipes

1.0 Introduction

Let’s start easy first. In this chapter, you’ll go through the essential recipes to begin coding in Go, installing Go, and then writing some simple Go code. You will go through the fundamentals, from using external libraries and handling errors to simple testing and logging events. Some of these recipes are intentionally concise—more detail about them in later chapters. If you’re already familiar with the basics of Go, you can skip this chapter altogether.

1.1 Installing Go

Problem

You want to install Go and prepare the environment for Go development.

Solution

Go to the Go website and download the latest, greatest version of Go. Then follow the installation instructions to install Go.

Discussion

First, you need to go to the Go download site. You can choose the correct version according to your operating system and hardware and download the right package.

There are a couple of ways to install Go—you can either install the prebuilt binaries for your platform or compile it from the source. Most of the time, there is no need to compile it from the source unless you can’t find the appropriate prebuilt binaries for your operating system. Even then, you can usually use your operating system’s package manager to install Go instead.

MacOS

Open the downloaded package file and follow the prompts to install. Go will be installed at /usr/local/go, and your PATH environment variable should also have /usr/local/go/bin added to it.

You can also choose to install using Homebrew, which is usually a version or two behind. To do this, run this from the command line:

$ brew update && brew install golang

You can set up the PATH later as you like.

Linux

Extract the downloaded archive file into /usr/local, which should create a go directory. For example, run this from the command line (replace the Go version as necessary):

$ rm -rf /usr/local/go && tar -C /usr/local -xzf go1.20.1.linux-amd64.tar.gz

You can add /usr/local/go/bin into your PATH as needed by adding this line to your $HOME/.profile:

export PATH=$PATH:/usr/local/go/bin

The changes will be made the next time you log in, or if you want it to take effect immediately, run source on your profile file:

$ source $HOME/.profile

Windows

Open the downloaded MSI installer file and follow the prompts to install Go. By default, Go will be installed to Program Files or Program Files (x86), but you can always change this.

Build from source

You shouldn’t build from source unless you cannot find the prebuilt binaries for your operating system. However, if you need to, extract the source files to an appropriate directory first, then run these commands from the command line:

$ cd src
$ ./all.bash

If you’re building in Windows, use all.bat instead. The assumption here is that the compilers are already in place. If not, and if you need a deeper dive into installing Go, go to the Installing Go site for details.

If all goes well, you should see something like this:

ALL TESTS PASSED

---
Installed Go for linux/amd64 in /home/you/go.
Installed commands in /home/you/go/bin.
*** You need to add /home/you/go/bin to your $PATH. ***

To check if you have installed Go, you can run the Go tool with the version option to see the installed version:

% go version

If you have correctly installed Go, you should see something like this:

go version go1.20.1 darwin/amd64

1.2 Playing Around with Go

Problem

You want to write and execute Go code without downloading and installing Go.

Solution

Use the Go Playground to run your Go code.

Discussion

The Go Playground runs on Google’s servers, and you can run your program inside a sandbox. Go to the Go Playground URL on your browser. This online environment lets you play with Go code, running the latest Go version (you can switch to a different version). The same web page returns output but only supports standard output and standard error (see Figure 1-1).

In a pinch, the Go Playground is a good option to test some Go code. You can also use the Go Playground to share executable code directly.

The Go Playground
Figure 1-1. Go Playground

1.3 Writing a Hello World Program

Problem

You want to create a simple Hello World program in Go.

Solution

Write the Hello World code in Go, build, and run it.

Discussion

Here’s a straightforward Hello World program. Put this in a file named hello.go, in a directory named hello:

package main

import "fmt"

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

The first line defines the package this code runs in. Functions are grouped in packages, and code files in the same packages are all in the same directory. The main package is special because it tells Go this should be compiled as an executable program. We also import the fmt package, which is part of the standard library.

The main package must have a main function, which is where the execution of the program starts. In the body of the main function, we use the Println function in the fmt package to print out the string “Hello, World!” The fmt package is part of the Go standard library. Go has a pretty good standard library that covers most of what you will typically need. Visit Go’s Standard library to find out what the standard library has.

You can run this program immediately by running it from the command line:

$ go run hello.go

You should see this on your screen:

Hello, World!

You can also compile it into an executable file by running this command:

$ go build

This will create an executable file named hello (macOS or Linux) or hello.exe (Windows) in the same directory. The name hello follows the name of the directory it’s in. You can change the output name with this command:

$ go build -o helloworld

This will create the executable file named helloworld (macOS or Linux) or helloworld.exe (Windows).

1.4 Using an External Package

Problem

You want to import a package from an external library.

Solution

Use the import keyword to import an external package.

Discussion

Let’s say you want to display the size of a file. You get the exact file size, but it’s a relatively large number that is not intuitive to users. You want to easily display the file size without doing much of the mathematics yourself.

You searched the internet and found this interesting third-party, open source package at https://github.com/dustin/go-humanize, and it does all that you want. How can you include it and use the functions in the package?

You can do this just like importing a standard library, but instead of using the package name, use the package location. Normally you’d expect the name of the package to be go-humanize. However, the package name used in the code itself is humanize. This is because the package name, as defined by the author of this package, is humanize:

package main

import (
	"fmt"

	"github.com/dustin/go-humanize"
)

func main() {
	var number uint64 = 123456789
	fmt.Println("Size of file is", humanize.Bytes(number))
}

In many cases, the last directory of the location is the name of the package, but it’s not always necessarily so. You can even change the way you call functions in the external package:

package main

import (
	"fmt"

	human "github.com/dustin/go-humanize"
)

func main() {
	var number uint64 = 123456789
	fmt.Println("Size of file is", human.Bytes(number))
}

Notice that you now use the name human when calling the functions. Why does Go allow this? It’s because there might be conflicting package names since Go doesn’t control how the package is named, nor does it have a centralized repository of packages.

If you try to run this directly, assuming the source code is in a file named human.go:

$ go run human.go

you will see this error message:

human.go:6:2: no required module provides package github.com/dustin/go-humanize:
go.mod file not found in the current directory or any parent directory; see
'go help modules'

This is because Go doesn’t know where to find the third-party package (unlike the standard library packages); you must tell it. To do this, you first need to create a Go module:

$ go mod init github.com/sausheong/humanize

This creates a Go module with the module path github.com/sausheong/humanize, specified in a go.mod file. This file provides Go with information on the various third-party packages to include. Then you can get the go-humanize package using the go tool again:

$ go get github.com/dustin/go-humanize

This will add the third-party package to the module. To clean up, you can run the following:

$ go mod tidy

This will clean up the go.mod file. You will get back to Go modules in Chapter 2.

1.5 Handling Errors

Problem

You want to take care of errors, which will inevitably occur because things never happen as expected.

Solution

Check if a function returns an error and handle it accordingly.

Discussion

Error handling in Go is important. Go is designed such that you need to check for errors explicitly. Functions that could go wrong will return a built-in type called error.

Functions that convert a string to a number (like ParseFloat and ParseInt) can get into trouble because the string might not be a number, so it will always return an error. For example, in the strconv package, functions that convert a number to a string (like FormatFloat and FormatInt) do not return errors. This is because you are forced to pass in a number, and whatever you pass in will be converted into a string.

Take a look at the following code:

func main() {
	str := "123456789"
	num, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		panic(err)
	}
	fmt.Println("Number is", num)
}

The ParseInt function takes in a string (and some other parameters) and returns a number num and an error err. You should inspect the err to see if the ParseInt function returns anything. If there is an error, you can handle it as you prefer. In this example, you panic, which exits the program.

If all goes well, this is what you should see:

Number is 123456789

If you change str to "abcdefg", you will get this:

panic: strconv.ParseInt: parsing "abcdefghi": invalid syntax

goroutine 1 [running]:
main.main()
	/Users/sausheong/work/src/github.com/sausheong/gocookbook/ch01_general/
	main.go
	+0xae
exit status 2

Of course, you can handle it differently or even ignore it if you want. You’ll get in-depth with error handling in Chapter 3.

1.6 Logging Events

Problem

You want to record events that happen during the execution of your code.

Solution

Use the log package in the Go standard library to log events.

Discussion

Logging events during code execution gives you a good view of how the code is doing during execution. This is important, especially during long-running programs. Logs help to determine issues with the execution and also the state of the execution. Here is a simple example used earlier:

package main

import (
	"fmt"
	"log"
	"strconv"
)

func main() {
	str := "abcdefghi"
	num, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		log.Fatalln("Cannot parse string:", err)
	}
	fmt.Println("Number is", num)
}

When you encounter an error being returned from calling the strconv.ParseInt function, you call log.Fatalln, which is equivalent to logging the output to the screen and exiting the application. As you can see, logging to the screen also adds the date and time the event occurred:

021/11/18 09:19:35 Cannot parse string: strconv.ParseInt: parsing "abcdefghi":
invalid syntax
exit status 1

By default, the log goes to standard out, which means it will print to the terminal screen. You can easily convert it to log to a file instead, or even multiple files. More about that in Chapter 4.

1.7 Testing Your Code

Problem

You want to test your code’s functionality to ensure it’s working the way you want.

Solution

Use Go’s built-in testing tool to do functional tests.

Discussion

Go has a useful built-in testing tool that makes testing easier since you don’t need to add another third-party library. You’ll convert the previous code to a function while leaving your main function free:

func main() {
}

func conv(str string) (num int64, err error) {
	num, err = strconv.ParseInt(str, 10, 64)
	return
}

You’ll be doing some testing on this function. To do this, create a file that ends with _test.go in the same directory. In this case, create a conv_test.go file.

In this file, you can write the various test cases you want. Each test case can correspond to a function that starts with Test and takes in a single parameter of type testing.T.

You can add as many test cases as you want across all multiple test files, as long as they all end with _test.go:

package main

import "testing"

func TestConv(t *testing.T) {
	num, err := conv("123456789")
	if err != nil {
		t.Fatal(err)
	}
	if num != 123456789 {
		t.Fatal("Number don't match")
	}
}

func TestFailConv(t *testing.T) {
	_, err := conv("")
	if err == nil {
		t.Fatal(err)
	}
}

Within the test functions, you call the conv function that you wanted to test, passing it whatever test data you want. If the function returns an error or the returned value doesn’t match what you expect, you call the Fatal function, which logs a message and then ends the execution of the test.

Try it: run this from the command line. The flag -v is to increase its verbosity so you can see how many test cases are executed and passed:

$ go test -v

This is what you see:

=== RUN   TestConv
--- PASS: TestConv (0.00s)
=== RUN   TestFailConv
--- PASS: TestFailConv (0.00s)
PASS
ok  	github.com/sausheong/gocookbook/ch01_general

As you can see, all your cases pass. Now make a small change in your conv function:

func conv(str string) (num int64, err error) {
	num, err = strconv.ParseInt(str, 2, 64)
	return
}

Instead of parsing the number as base 10, you use base 2. Rerun the test:

=== RUN   TestConv
    general_test.go:8: strconv.ParseInt: parsing "123456789": invalid syntax
--- FAIL: TestConv (0.00s)
=== RUN   TestFailConv
--- PASS: TestFailConv (0.00s)
FAIL
exit status 1
FAIL	github.com/sausheong/gocookbook/ch01_general

You see that the TestConv test case failed because it no longer returns the expected number. However, the second test case passes because it tests for a failure and it encountered it.

Testing is covered more extensively in Chapter 18.

Get Go Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.