In this article, we shall be discussing how to return and handle errors effectively using custom and inbuilt Golang functions, with help of practical examples.
Golang return error
An error is basically a fault that occurs in a program execution flow. These errors can be of various natures:- Caused by programmers through code syntax and interface errors
, system-related Resources and Runtime errors
, algorithm-related logic and arithmetic errors
. Which later are solved through debugging process.
In Golang ,The Error is an interface that holds Error() string
method. Its implemented as follows
type error interface {
Error() string
}
In an nutshell, when the Error() method is called, it's return value is in form of string datatype. Through the use of inbuilt Go functions of the fmt and errors packages, we can construct the kind of error message to be displayed. Below is an example to construct Errors using fmt.Error()
in Golang, i.e you want to read a file from a given path, unfortunate the file doesn't exist or the path given is invalid. For example:=
package main
import (
"fmt"
"os"
)
func ReadFile(file string) error {
dataFile, err := os.ReadFile(file)
if err != nil {
return fmt.Errorf("An error occurred while Reading the file: open : %v", err)
}
fmt.Println(string(dataFile))
return nil
}
func main() {
resultsErr := ReadFile("")
if resultsErr != nil {
fmt.Printf("%v", resultsErr)
}
}
Output:
With the file attached ensure you replace the ReadFile("test.txt")
$ go run main.go
Hello
without file attached
$ go run main.go
An error occurred while Reading the file: open: no such file or directory
Explanation:- In the above code, ReadFile() error{}
function returns an error which is nil whenever no error encountered. In Golang, the Error return value is nil as the default, or “zero”. Notice that checking if err != nil{} is the idiomatic way to determine if an error was encountered in Golang syntax, in this function we are returning the error only, handling the file data within the function. fmt.Error()
enables us to customize the kind of message to be displayed. These messages are always in a lowercase format and don't end with punctuation.
In Golang there are numerous ways to return and handle errors Namely:=
- Casting Errors
- Error wrapping mechanism
- Panic, defer and recover
Different methods of error handling in Go Func
Method 1:- Casting Errors
Casting errors is a way of defining custom and expected errors, with golang we can make use of erros.Isand errors.As() error functions to cast different types of errors. i.e,errors.Is we create a custom error of a particular type and check If the error matches the specific type the function will return true, if not it will return false.
package main
import (
"errors"
"fmt"
"io/fs"
"os"
)
var fileNotFound = errors.New("The file doesn't exist")
func ReadFile(file string) error {
dataFile, readErr := os.ReadFile(file)
if readErr != nil {
if errors.Is(readErr, fs.ErrNotExist) {
return fmt.Errorf("this fileName %s doesn't exist ", file)
} else {
return fmt.Errorf("Error occured while opening the file : %w", readErr)
}
}
fmt.Println(string(dataFile))
return nil
}
func main() {
fileName := os.Args[1]
if fileName != "" {
resultsError := ReadFile(fileName)
if resultsError != nil {
fmt.Printf("%v", resultsError)
}
} else {
fmt.Println("the file name cant be empty")
}
}
Output:
$ go run main.go "new"
this fileName new doesn't exist
Explanation:- We are using errors.Is(readErr, fs.ErrNotExist) {} to check if the file passed exist, if it doesn't exist we return custom message as shown above. we can also use the custom error message such as errors.New() to create expected error and handle it as errors.Is(readErr, fileNotFound) {} the return values will be the same.
Method 2:- Error wrapping
Wrapping is a way of using other errors within a function to provide more context and detailed error messages.
fmt.Error()
function enable us to create a wrapped errors with use of %w flag. The %w
flag is used for inspecting and unwrapping errors.
In this subtitles we can incorporate other functions from errors
package used to handle errors, namely:- errors.As, errors.Is, errors.Unwrap
functions. errors.As
is used to cast a specific error type, i.e func As(err error, target any) bool{}
, also, the errors.Unwrap
is used to inspect and expose the underlying errors in a program,i.e func (e *PathError)Unwrap()error{ return e.Err}
, Furthermore the errors.Is
mostly for comparing error value against the sentinel value if is true or false, i.e func Is(err,target error) bool{}
.
Example of Error Wrapping
package main
import (
"errors"
"fmt"
"os"
)
func ReadFile(file string) error {
dataFile, readErr := os.ReadFile(file)
var pathError *os.PathError
if readErr != nil {
if errors.As(readErr, &pathError) {
return fmt.Errorf("this fileName %s doesn't exist and failed opening file at this path %v", file, pathError.Path)
}
return fmt.Errorf("Error occured while opening the file : %w", readErr)
}
fmt.Println(string(dataFile))
return nil
}
func main() {
fileName := os.Args[1]
if fileName != "" {
resultsError := ReadFile(fileName)
if resultsError != nil {
fmt.Printf("%v", resultsError)
}
} else {
fmt.Println("the file name can't be empty")
}
}
Output:
With the file attached ensure you replace the ReadFile("test.txt")
$ go run main.go
Hello
without file attached
$ go run main.go ""
the file name can't be empty
$ go run main.go next.txt
this fileName news.txt doesn't exist and failed opening file at this path news.txt
Explanation:- In the above code we have used fmt.Errorf() functions to format the error message to be displayed and wrapping error using a custom error message with wrap function errors.Is() which checks if the path exists. You can avoid unnecessary error wrapping and handle it once.
Method-3: Using Panic, Defer and Recover
We have covered this topic in detail in a separate article Golang panic handing [capture, defer, recover, log]
Summary
At this point in this article, you have learned various ways to return and handle errors in the Golang function. In Go, Errors are considered to be a very lightweight piece of data that implements the Error interface. Custom errors in Go help in debugging and signaling where the error has occurred from. Error tracing is easy as compared to other programming languages. Golang application development, error handling is very critical and helps one not only during debugging but also to monitor the application behavior. We recommend you to read more about panic, recover and defer mechanism of error handling as well.
References
error-handling in Go
Working with errors in golang
Errors