Error in Go

In this tutorial, you will learn about using errors and how to create your own custom errors. Go uses an error handling method similar to C language without any exceptions but in other languages like JAVA uses try/catch exceptions. In-go error handling is simple with predefined Error types.
In addition, we will also learn about using panic to generate panic errors and recover to overcome them in later tutorials.

What is Error in Golang?

An error is an unexpected action or event occurring in an executing go program. The error in the program affects the normal flow of execution and results in sudden termination. Errors are also referred to as bugs. The method used to find and resolve bugs in programming languages is known as debugging.

Go allows packages and tools to handle errors i.e. mistakes. To implement the functions to manipulate error going provides Error Packages. Go returns an error object for trivial mistakes in functions and methods. It's possible that the error object is the only or last return value. If there is no problem with the function, the error object is nil. If we receive any errors in the calling statement, we should always verify them.

 Go programming language does not support a mechanism to handle exceptions, like in many other languages exceptions cannot be thrown in Golang. Instead of that Golang incorporates new error handling mechanisms like panic and recovery.

How to declare errors in Golang?

Error is a type. An error has one property in the form of an Error() method, this method returns the details of the error message in a string. Error is a type whose 


// The built-in error interface is a regular interface for handling errors. 
// where nil means no errors. 
type error interface { 
     Error() string 
     }

As you can see, an error handler is actually an interface that contains a simple Error() method whose return value is a string.

This definition tells us that to implement error handling, all that is needed is to return a simple string to the Error() method.  


func main() {
    conent, err := openFile()
    if err != nil {
        fmt.Println(err)
    } else {
        fmt.Println(string(conent))
    }
}

In case err! = nil (presence of an error is detected) it terminates from its execution otherwise continues with the normal flow of execution.

In Go, many functions return more than one value. Usually, one of the returns is of type error. An example is the strconv.Atoi() function. This function is used to convert string data to numeric. This function returns 2 return values. The first return value is the result of the conversion, and the second return value is an error. When the conversion is smooth, the second return value will be nil. Meanwhile, when the conversion fails, the cause can be immediately identified from the error returned.

Below is an example of a simple program to detect input from a user, whether numeric or not. From here we will learn about the use of errors.  


package main
import (
    "fmt"
    "strconv"
)

func main() {
    var input string
    fmt.Print("Type some number: ")
    fmt.Scanln(&input)

    var number int
    var err error
    number, err = strconv.Atoi(input)

    if err == nil {
        fmt.Println(number, "is number")
    } else {
        fmt.Println(input, "is not number")
        fmt.Println(err.Error())
    }
}

Run the program, it says "Type some number: ". Type a free number, if it is then enter.

Output:


Type some number:  is not number
strconv.Atoi: parsing "": invalid syntax

The fmt.Scanln(&input) statement is used to capture input that was typed by the user before he presses enter, then saves it as a string to the input variable.

Then the variable is converted to a numeric type using strconv.Atoi(). The function returns 2 data, accommodated by number and err.

The first data (number) contains the conversion result. And the second data err, contains the error information (if an error occurred during the conversion process).

After that check is done, when there is no error, the number is displayed. And if there is an error, the input is displayed along with the error message. The error message can be obtained from the Error() method of the error type.

How to Create Custom Errors?

In addition to taking advantage of the errors returned by an available internal function, we can also create our own error objects using the errors.New() function (must import package errors first).

The following example shows how to create a custom error. First, prepare a function with the name validate(), which will later be used to check the input, whether the input is empty or not. When empty, a new error will be generated.


Package main

import (
    "errors"
    "fmt"
    "strings"
)

func validate(input string) (bool, error) {
    if strings.TrimSpace(input) == "" {
        return false, errors.New("cannot be empty")
    }
    return true, nil
}

Next in the main function, create a simple process to capture user input. Take advantage of the validate() function to check the input.

In the above program the function error.New() defines a new error type from the error package and within the parentheses passes appropriate error message.


func main() {
    var name string
    fmt.Print("Type your name: ")
    fmt.Scanln(&name)

    if valid, err := validate(name); valid {
        fmt.Println("hello", name)
    } else {
        fmt.Println(err.Error())
    }
}

The validate() function returns 2 records. The first data is a bool value which indicates whether the input is valid or not. The 2nd data is the error message (if the input is invalid).

The strings.TrimSpace() function is used to remove space characters before and after a string. This is needed because the user can just enter a space and then enter.

When the input is invalid, a new error is created using the errors.New() function. In addition, an error object can also be created via the fmt.Errorf() function.  

Program for custom error


package main

import (
    "errors"
    "fmt"
    "strings"
)

func validate(input string) (bool, error) {
    if strings.TrimSpace(input) == "" {
        return false, errors.New("cannot be empty")
    }
    return true, nil
}
func main() {
    var name string
    fmt.Print("Type your name: \n")
    fmt.Scanln(&name)

    if valid, err := validate(name); valid {
        fmt.Println("hello", name)
    } else {
        fmt.Println(err.Error())
    }

Output:


Type your name: 
cannot be empty

ioutil.ReadFile in Golang

In go programming language ioutil.ReadFile reads a file and returns its contents. A call sets the err to nil if its execution was successful.
Consider a read file with test.txt with text as shown below  

GO : Error

Program to read file test.txt through go code is given below


package main
import (
 "fmt"
 "io/ioutil"
 "log"
)

var path = "C:/Users/Desktop/Golang/Tutorials/test.txt"

func main() {

 content, err := ioutil.ReadFile(path)

 if err != nil {
  log.Fatal(err)
 }

 fmt.Println(string(content))
}  }
 }
 if err != nil {
  panic(err)
 }

 fmt.Println("==> file successfull")
 fmt.Println(string(text))
}

Output:


Windows PowerShell
Copyright (C) 2014 Microsoft Corporation. All rights reserved.

PS C:\Users\Desktop\Golang> go run errorread.go
hello welcome to golang

An error occurs by setting a wrong path that provides an error indication. In order to identify the difference, we use the same program with a change in the test. filename to text. file in the path.


package main
import (
 "fmt"
 "io/ioutil"
 "log"
)

var path = "C:/Users/Desktop/Golang/Tutorials/text.txt"

func main() {

 content, err := ioutil.ReadFile(path)

 if err != nil {
  log.Fatal(err)
 }

The log. fatal function prints the error to the console and terminates the program by calling os.Exit.


if err != nil {
  log.Fatal(err)
 }

Output:


PS C:\Users\Desktop\Golang> go run errorread.go
2022/01/08 12:38:49 open C:/Users/Desktop/Golang/Tutorials/text.txt: The system cannot find the file specified.
exit status 1

Note: To understand the read file concept refer to the tutorial file

Golang errors. Is

The errors. Is function checks if the error is of the specified type.
Let us understand with an example.  


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

var path = "C:/Users/Desktop/Golang/Tutorials/test.txt"

func main() {
 if _, err := os.Open(path); err != nil {
  if errors.Is(err, os.ErrNotExist) {
   log.Fatal("file does not exist\t", err)
  } else if errors.Is(err, os.ErrPermission) {
   log.Fatal("insufficient permissions\t", err)

  } else {
   log.Fatal(err)
  }
 }

 fmt.Println("...")
}

In the above example, we check the error is of type os.ErrNotExist or os.ErrPermission are checked

Output:


Windows PowerShell
Copyright (C) 2014 Microsoft Corporation. All rights reserved.

PS C:\Users\Desktop\Golang> go run isfun.go
...

Working of Is() function of error package

Let us understand with a program


package main
import (
 "errors"
 "fmt"
)
 const Wr

var ErrInput = errors.New("bad input")

func validateInput(input string) error {
 if input == WrongInput {
  return fmt.Errorf("validateInput: %w", ErrInput)
 }
 return nil
}

func main() {
 input := WrongInput

 err := validateInput(input)
 if errors.Is(err, ErrInput) {
  fmt.Println("error due to wrong input")
 }
}

In the above program, you can observe the function validate input returns an error for WrongInput. This error is ErrInput wrapped in an error created by fmt.Errorf(). Using the Is(err, target error) bool function, we can detect the ErrInput even if it is wrapped since this function checks if any error in the chain of wrapped errors matches the target. Therefore, this form should be preferable to comparison if err == ErrInput.

Output:


error due to wrong input

Program exited.