Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Mastering Go

You're reading from   Mastering Go Create Golang production applications using network libraries, concurrency, machine learning, and advanced data structures

Arrow left icon
Product type Paperback
Published in Aug 2019
Publisher Packt
ISBN-13 9781838559335
Length 798 pages
Edition 2nd Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Mihalis Tsoukalos Mihalis Tsoukalos
Author Profile Icon Mihalis Tsoukalos
Mihalis Tsoukalos
Arrow right icon
View More author details
Toc

Table of Contents (15) Chapters Close

1. Go and the Operating System 2. Understanding Go Internals FREE CHAPTER 3. Working with Basic Go Data Types 4. The Uses of Composite Types 5. How to Enhance Go Code with Data Structures 6. What You Might Not Know About Go Packages and Functions 7. Reflection and Interfaces for All Seasons 8. Telling a UNIX System What to Do 9. Concurrency in Go – Goroutines, Channels, and Pipelines 10. Concurrency in Go – Advanced Topics 11. Code Testing, Optimization, and Profiling 12. The Foundations of Network Programming in Go 13. Network Programming – Building Your Own Servers and Clients 14. Machine Learning in Go 15. Other Books You May Enjoy

Error handling in Go

Errors and error handling are two very important Go topics. Go likes error messages so much that it has a separate data type for errors, named error. This also means that you can easily create your own error messages if you find that what Go gives you is not adequate.

You will most likely need to create and handle your own errors when you are developing your own Go packages.

Please note that having an error condition is one thing, but deciding how to react to an error condition is a totally different thing. Putting it simply, not all error conditions are created equal, which means that some error conditions might require that you immediately stop the execution of a program, whereas other error situations might require printing a warning message for the user to see while continuing the execution of the program. It is up to the developer to use common sense and decide what to do with each error value the program might get.

Errors in Go are not like exceptions or errors in other programming languages; they are normal Go objects that get returned from functions or methods just like any other value.

The error data type

There are many scenarios where you might end up having to deal with a new error case while you are developing your own Go application. The error data type is here to help you to define your own errors.

This subsection will teach you how to create your own error variables. As you will see, in order to create a new error variable, you will need to call the New() function of the errors standard Go package.

The example Go code to illustrate this process can be found in newError.go and will be presented in two parts. The first part of the program is as follows:

package main 
 
import ( 
    "errors" 
    "fmt" 
) 
 
func returnError(a, b int) error { 
    if a == b { 
        err := errors.New("Error in returnError() function!") 
        return err 
    } else { 
        return nil 
    } 
} 

There are many interesting things happening here. First of all, you can see the definition of a Go function other than main() for the first time in this book. The name of this new naive function is returnError(). Additionally, you can see the errors.New() function in action, which takes a string value as its parameter. Lastly, if a function should return an error variable but there is not an error to report, it returns nil instead.

You will learn more about the various types of Go functions in Chapter 6, What You Might Not Know About Go Packages and Go Functions.

The second part of newError.go is the following:

func main() { 
    err := returnError(1, 2) 
    if err == nil { 
        fmt.Println("returnError() ended normally!") 
    } else { 
        fmt.Println(err) 
    } 
 
    err = returnError(10, 10) 
    if err == nil { 
        fmt.Println("returnError() ended normally!") 
    } else { 
        fmt.Println(err) 
    } 
 
    if err.Error() == "Error in returnError() function!" { 
        fmt.Println("!!") 
    } 
} 

As the code illustrates, most of the time, you need to check whether an error variable is equal to nil and then act accordingly. What is also presented here is the use of the errors.Error() function, which allows you to convert an error variable into a string variable. This function lets you compare an error variable with a string variable.

It is considered good practice to send your error messages to the logging service of your UNIX machine, especially when a Go program is a server or some other critical application. However, the code of this book will not follow this principle everywhere in order to avoid filling your log files with unnecessary data.

Executing newError.go will produce the following output:

$ go run newError.go
returnError() ended normally!
Error in returnError() function!
!!
  

If you try to compare an error variable with a string variable without first converting the error variable to a string variable, the Go compiler will create the following error message:

# command-line-arguments
./newError.go:33:9: invalid operation: err == "Error in returnError() function!" (mismatched types error and string)
  

Error handling

Error handling in a very important feature of Go because almost all Go functions return an error message or nil, which is the Go way of saying whether there was an error condition while executing a function. You will most likely get tired of seeing the following Go code not only in this book but also in every other Go program you can find on the internet:

if err != nil { 
    fmt.Println(err) 
    os.Exit(10) 
} 
Please do not confuse error handling with printing to error output because they are two totally different things. The former has to do with Go code that handles error conditions, whereas the latter has to do with writing something to the standard error file descriptor.

The preceding code prints the generated error message on the screen and exits your program using os.Exit(). Please note that you can also exit your program by calling the return keyword inside the main() function. Generally speaking, calling os.Exit() from a function other than main() is considered a bad practice. Functions other than main() tend to return the error message before exiting, which is handled by the calling function.

Should you wish to send the error message to the logging service instead of the screen, you should use the following variation of the preceding Go code:

if err != nil { 
    log.Println(err) 
    os.Exit(10) 
} 

Lastly, there is another variation of the preceding code that is used when something really bad has happened and you want to terminate the program:

if err != nil { 
    panic(err) 
    os.Exit(10) 
} 

Panic is a built-in Go function that stops the execution of a program and starts panicking! If you find yourself using panic too often, you might want to reconsider your Go implementation. People tend to avoid panic situations in favor of errors wherever possible.

As you will see in the next chapter, Go also offers the recover function, which might be able to save you from some bad situations. For now, you will need to wait for Chapter 2, Understanding Go Internals, to learn more about the power of the panic and recover function duo.

It is now time to see a Go program that not only handles error messages generated by standard Go functions, but also defines its own error message. The name of the program is errors.go and it will be presented to you in five parts. As you will see, the errors.go utility tries to improve the functionality of the cla.go program you saw earlier in this chapter by examining whether its command-line arguments are acceptable floats.

The first part of the program is as follows:

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

This part of errors.go contains the expected import statements.

The second portion of errors.go comes with the following Go code:

func main() { 
    if len(os.Args) == 1 { 
        fmt.Println("Please give one or more floats.") 
        os.Exit(1) 
    } 
 
    arguments := os.Args 
    var err error = errors.New("An error") 
    k := 1 
    var n float64 

Here, you create a new error variable named err in order to initialize it with your own value.

The third part of the program is as follows:

    for err != nil { 
        if k >= len(arguments) { 
        fmt.Println("None of the arguments is a float!") 
        return 
        } 
        n, err = strconv.ParseFloat(arguments[k], 64) 
        k++ 
    } 
 
    min, max := n, n 

This is the trickiest part of the program because if the first command-line argument is not a proper float, you will need to check the next one and keep checking until you find a suitable command-line argument. If none of the command-line arguments are in the correct format, errors.go will terminate and print a message on the screen. All this checking happens by examining the error value that is returned by strconv.ParseFloat(). All this code is just for the accurate initialization of the min and max variables.

The fourth portion of the program comes with the following Go code:

    for i := 2; i < len(arguments); i++ { 
        n, err := strconv.ParseFloat(arguments[i], 64) 
        if err == nil { 
        if n < min { 
            min = n 
        } 
        if n > max { 
            max = n 
        } 
        } 
    } 

Here, you just process all the right command-line arguments in order to find the minimum and maximum floats among them.

Finally, the last code portion of the program deals with just printing out the current values of the min and max variables:

    fmt.Println("Min:", min) 
    fmt.Println("Max:", max) 
} 

As you can see from the Go code of errors.go, most of its code is about error handling rather than about the actual functionality of the program. Unfortunately, this is the case with most modern software developed in Go, as well as most other programming languages.

If you execute errors.go, you will get the following kind of output:

$ go run errors.go a b c
None of the arguments is a float!
$ go run errors.go b c 1 2 3 c -1 100 -200 a
Min: -200
Max: 100
  
lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at £16.99/month. Cancel anytime