Book Image

Mastering Go – Third Edition - Third Edition

By : Mihalis Tsoukalos
5 (2)
Book Image

Mastering Go – Third Edition - Third Edition

5 (2)
By: Mihalis Tsoukalos

Overview of this book

Mastering Go is the essential guide to putting Go to work on real production systems. This freshly updated third edition includes topics like creating RESTful servers and clients, understanding Go generics, and developing gRPC servers and clients. Mastering Go was written for programmers who want to explore the capabilities of Go in practice. As you work your way through the chapters, you’ll gain confidence and a deep understanding of advanced Go concepts, including concurrency and the operation of the Go Garbage Collector, using Go with Docker, writing powerful command-line utilities, working with JavaScript Object Notation (JSON) data, and interacting with databases. You’ll also improve your understanding of Go internals to optimize Go code and use data types and data structures in new and unexpected ways. This essential Go programming book will also take you through the nuances and idioms of Go with exercises and resources to fully embed your newly acquired knowledge. With the help of Mastering Go, you’ll become an expert Go programmer by building Go systems and implementing advanced Go techniques in your projects.
Table of Contents (17 chapters)
14
Other Books You May Enjoy
15
Index

The error data type

Go provides a special data type for representing error conditions and error messages named error—in practice, this means that Go treats errors as values. In order to program successfully in Go, you should be aware of the error conditions that might occur with the functions and methods you are using and handle them accordingly.

As you already know from the previous chapter, Go follows the next convention about error values: if the value of an error variable is nil, then there was no error. As an example, let us consider strconv.Atoi(), which is used for converting a string value into an int value (Atoi stands for ASCII to Int). As specified by its signature, strconv.Atoi() returns (int, error). Having an error value of nil means that the conversion was successful and that you can use the int value if you want. Having an error value that is not nil means that the conversion was unsuccessful and that the string input is not a valid int value.

If you want to learn more about strconv.Atoi(), you should execute go doc strconv.Atoi in your terminal window.

You might wonder what happens if you want to create your own error messages. Is this possible? Should you wish to return a custom error, you can use errors.New() from the errors package. This usually happens inside a function other than main() because main() does not return anything to any other function. Additionally, a good place to define your custom errors is inside the Go packages you create.

You will most likely work with errors in your programs without needing the functionality of the errors package. Additionally, you do not need to define custom error messages unless you are creating big applications or packages.

If you want to format your error messages in the way fmt.Printf() works, you can use the fmt.Errorf() function, which simplifies the creation of custom error messages—the fmt.Errorf() function returns an error value just like errors.New().

And now we should talk about something important: you should have a global error handling tactic in each application that should not change. In practice, this means the following:

  • All error messages should be handled at the same level, which means that all errors should either be returned to the calling function or be handled at the place they occurred.
  • It should be clearly documented how to handle critical errors. This means that there will be situations where a critical error should terminate the program and other times where a critical error might just create a warning message onscreen.
  • It is considered a good practice to send all error messages to the log service of your machine because this way the error messages can be examined at a later time. However, this is not always true, so exercise caution when setting this up—for example, cloud native apps do not work that way.

The error data type is actually defined as an interface—interfaces are covered in Chapter 4, Reflection and Interfaces.

Type the following code in your favorite text editor and save it as error.go in the directory where you put the code for this chapter. Using ch02 as the directory name is a good idea.

package main
import (
    "errors"
    "fmt"
    "os"
    "strconv"
)

The first part is the preamble of the program—error.go uses the fmt, os, strconv, and errors packages.

// Custom error message with errors.New()
func check(a, b int) error {
    if a == 0 && b == 0 {
        return errors.New("this is a custom error message")
    }
    return nil
}

The preceding code implements a function named check() that returns an error value. If both input parameters of check() are equal to 0, the function returns a custom error message using errors.New()—otherwise it returns nil, which means that everything is OK.

// Custom error message with fmt.Errorf()
func formattedError(a, b int) error {
    if a == 0 && b == 0 {
        return fmt.Errorf("a %d and b %d. UserID: %d", a, b, os.Getuid())
    }
    return nil
}

The previous code implements formattedError(), which is a function that returns a formatted error message using fmt.Errorf(). Among other things, the error message prints the user ID of the user that executed the program with a call to os.Getuid(). When you want to create a custom error message, using fmt.Errorf() gives you more control over the output.

func main() {
    err := check(0, 10)
    if err == nil {
        fmt.Println("check() ended normally!")
    } else {
        fmt.Println(err)
    }
    err = check(0, 0)
    if err.Error() == "this is a custom error message" {
        fmt.Println("Custom error detected!")
    }
    err = formattedError(0, 0)
    if err != nil {
        fmt.Println(err)
    }
    i, err := strconv.Atoi("-123")
    if err == nil {
        fmt.Println("Int value is", i)
    }
    i, err = strconv.Atoi("Y123")
    if err != nil {
        fmt.Println(err)
    }
}

The previous code is the implementation of the main() function where you can see the use of the if err != nil statement multiple times as well as the use of if err == nil, which is used to make sure that everything was OK before executing the desired code.

Running error.go produces the next output:

$ go run error.go
check() ended normally!
Custom error detected!
a 0 and b 0. UserID: 501
Int value is -123
strconv.Atoi: parsing "Y123": invalid syntax

Now that you know about the error data type, how to create custom errors, and how to use error values, we'll continue with the basic data types of Go that can be logically divided into two main categories: numeric data types and non-numeric data types. Go also supports the bool data type, which can have a value of true or false only.