Quick Summary:

Golang Error Handling has been the talk of the town because of its unconventional approach, unlike other languages that utilize try…catch block. It was quite difficult for the developers to digest the new process of Golang error handling patterns.

Go’s way of error handling has also been questioned and criticized as it was entirely out of the box. But, after few months of frustration, the technique of Golang error best practices proved to be remarkable. In this blog, I’ll discuss the basics of Golang Error Handling with examples and why is it having a better approach than other languages. For simplifying the blog, I’ve classified it into sections.

Table of Contents

What are Error and Error Handling?

Before moving on to Golang Error Handling’s technique, I would like to discuss a bit about Error and Error handling.

Errors are defined as the unwanted and unusual conditions encountered in the program. It can be either compile time or run time. Various examples are – accessing a file that does not exist, a failed db connection, or abnormal user inputs. Anything could generate an error.

Now the process for predicting where your program could behave abnormally and the technique of implementing the solution for further diagnosis is Error Handling. You might be familiar try…catch block for handling errors in Java, PHP, or Python.

Now let’s start with Error handling in Golang.

Exploring Golang Error Handling

Getting familiar with new approaches has always been difficult, no matter how clean and straightforward it can be. And, when people get frustrated with such new methods, they start criticizing them. That’s what happened with Go. Developers were dealing with conventional techniques; thus, it was quite challenging for them to make room for Go’s way of error handling. Many proposals were made to change and improve Golang Error Handling patterns, as you can see in this image taken from github.

There’s a lot to learn about the methods of Go error handling but before that, I would like to discuss the built-in error type of Golang.

The Error Type

If you’ve ever coded in Go you would be quite familiar with the error type. Now, the question might arise what is this error type?

The error type is nothing but a type of an interface. It is the type given to the error variable which declares itself as a string.

The syntax looks something like this-

Copy Text
type error interface {
    Error() string
}

Exploring Golang Error Handling Patterns

Golang’s philosophy behind error handling is very straightforward – Don’t overlook errors; they are critically important. The syntax of func f() (value, error) is quite a piece of cake to learn and implement, even for those who have just started with Go.

Golang Error Handling techniques implicitly force the developers to use errors as first-class values of the functions. In case you have missed returning the error string from your function like this –

Copy Text
func getUsers() (*Users, error) { .... }

func main() {
    users, _ := getUsers()
}

Almost all the IDEs and linters will notice that you’ve missed returning the error and will make it salient for your fellow developers while reviewing the code. In this way, Golang doesn’t force you to use error as a first-class value of your function ‘explicitly,’ but neither does it allow you to overlook it.

Golang just provides a built-in error type due to which you don’t forget how critical these errors can be. If you choose not to fire any actions when the program encounters an error because of err != nil, you have to be prepared for the consequences; even Golang would be unable to save you! Let’s have one example of Golang error handling best practices.

Copy Text
if error := criticalOperation(); error != nil {
    // Not returning anything is a bad practice.   
log.Printf("Oops! Something went wrong in the program", error)
    // `return` your error message hereafter this line!
}

if error := saveData(data); error != nil {
    return fmt.Errorf("Data has been lost", error)
}

When err != nil is encountered while calling criticalOperation() and if you choose to log the error message instead of handling it intelligently, even Go won’t save your program from the errors. Golang just provides you how to return and use the errors; further, Error handling in golang handling the Go errors is entirely up to you.

Golang prefers to use the panic and recover method rather than throwing exceptions and using try…catch block. We will learn more about that later. I hope you now had a basic idea of Go error handling. Now, let’s see why the Error handling in golang is better than other languages. And for that, we need to learn a bit about how different languages handle their errors.

Do you need assistance to solve your Golang error?
Hire Golang developer from us to fix the bugs and fine-tune your Golang app user experience.

Throwing Exception: Error Handling Way of Other Programming Languages

Those developers familiar with Javascript frameworks, Java, Python, Ruby, and PHP, might better understand how these languages handle their errors. Look at this code snippet of how to throw an exception-

Copy Text
try {
    criticalDataOperation1();
    criticalDataOperation2();
    criticalDataOperation3();
} catch (err) {
    console.error(err);
}

While executing the function criticalDataOperations(), it will jump to the catch block if an error occurs, and console.log(err) will be performed. The function criticalOperations() doesn’t have to explicitly state the flow of error, for which it will jump the catch block. If any exception is thrown while executing these functions, then the program will directly log the error. And this is the advantage of exception-based programs: if you have forgotten to handle some exceptions, then also the stack trace will notice it at the run time and move forward to catch block.

Throwing exceptions is not the only way of error handling; Rust is also one of its types. Rust provides good pattern matching with simple syntax to search errors and acquire similar results like exceptions.

Isn’t it strange to digest why Golang didn’t utilize exceptions, a conventional way of error handling, and came up with such a unique approach? Let’s quench our curiosity and dive for the answer.

Why didn’t Golang utilize exceptions, a conventional way to handle errors?

Two key points that are kept in mind while Golang error handling is:

  • Keep it simple.
  • Plan where it can go wrong.

Golang tends to keep the syntax and usage of if err != nil as simple as possible. It returns (value, err) from the function to ensure how to handle the program’s failure. You don’t need to stress yourself with the complications of nested try…catch blocks. The practice of exception-based code never lets the developers search the actual errors; instead, they will throw the exception, which will be handled in the catch block.

Developers are forced to analyze every situation in exception-based languages and throw exceptions without adequately addressing them. Whereas, Golang return error handle your errors and return them as values from the functions.

Here are the advantages of Golang new error handling.

  • Transparent control-flow.
  • No interruption of sudden uncaught exceptions.
  • You have full control over the errors as they are considered as values – you can do whatever with that.
  • Simple syntax.
  • Easy implementation of error chains to take action on the error.

The last point might seem quite confusing to you. Let me make it simpler for you. The easy syntax of if err != nil allows you to chain the functions returning errors throughout the hierarchy of your program until you have reached the actual error, which has to be handled precisely. The practice of chaining the errors can be relatively easy to traverse and debug, even for your teammates.

Here is the example for error-chaining.

Copy Text
// controllers/users.go
if error := db.CreateUserforDB(user); error != nil {
    return fmt.Errorf("error while creating user: %w", error)
}

// database/users.go
func (db *Database) CreateUserforDB(user *User) error {
    ok, error := db.DoesUserExistinDB(user)
    if error != nil {
        return fmt.Errorf("error in db while checking: %w", err)
    }
    ...
}

func (db *Database) DoesUserExistinDB(user *User) error {
    if error := db.Connected(); error != nil {
        return fmt.Errorf("error while establishing connection: %w", err)
    }
    ...
}

func (db *Database) Connected() error {
    if !isInternetConnectionEstablished() {
        return errors.New("not connected to internet")
    }
    ...
}

The advantage of the above code is that every block has returned informative errors that can be easily understood and are responsible for those errors they are aware of. This kind of Golang handle error chaining helps your program not to break unexpectedly and makes traversing of errors less time taking. You can also choose to use the stack trace in your function and utilize this library for exploring various built-in functions.

So far, we have seen Golang Error Handling best practices and fundamental way of using if…err != nil. Do you remember I have used panic and recover before, let’s see what the fuss is about?

Golang Error Handling: Panic and Recover Mechanism

As I have mentioned before, Golang has panic and recover rather than try and catch blocks. You might have seen try…catch block so many times in the program, so I believe the exception handling is not so exceptionally handled – what an irony! Sometimes, developers use exception handling to throw a custom error message; this usage complicates runtime errors and custom errors (avoid such practices).

Whereas Golang has a different method for custom errors, we have learned so far, i.e., of throwing Golang a custom error message by returning the error as the function’s value. And panic and recover technique is used in exceptional cases only, unlike try and catch.

If there’s a truly exceptional case for which you have to use a panic scenario; it will stop the regular function’s flow and start panicking. When function func has called panic(), the func won’t be executed further though other deferred functions will be performed as expected.

Recover is the built-in function that frees the function from its panicking state. It is only used inside the deferred functions. While executing the function normally, recover will return nil without any other effects.

Here is a simple code snippet for better understanding.

Panicking

Copy Text
exceptionalCondition := true
if exceptionalCondition {
    panic("panicking!!")
}

Creating panic in the programs is more manageable than handling it.

Recover: To Rescue From Panic

Copy Text
func F() {
   defer func() {
     if error := recover(); error != nil {
	  fmt.Println("This is the error: ", err)
	}()
  //do whatever here...
}

You can add an anonymous function or make a custom function using defer keyword.

This was a high overview of what is panic and recover mechanism and how does it work.

Conclusion

Thus, this was all about Golang Error Handling basics, how it is better than languages, and a high overview of panic and recover mechanism. I hope that this blog has helped you the way you have expected it. Being a globally renowned Golang development company, we have established our reputation in providing best-in-class services to cater to your golang project requirements. Hire Golang developer from us and turn your idea into a reality that is suited to your business needs.

Explore Advanced Golang Tutorials

Explore Now

Build Your Agile Team

Hire Skilled Developer From Us

[email protected]

Your Success Is Guaranteed !

We accelerate the release of digital product and guaranteed their success

We Use Slack, Jira & GitHub for Accurate Deployment and Effective Communication.

How Can We Help You?