3

Error Handling in GO – Part 2

 2 years ago
source link: https://blog.anynines.com/error-handling-in-go-part-2/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

Error Handling in GO – Part 2

Posted by Lenny Händleron 14. October 2022

Error in Golang Part 2

Chapter 2: Using fmt.errorf to Wrap Sentinel Errors of the Interface

We finished the previous chapter of this series with returning Sentinel error values, and noticed that we may want to provide additional information to the user. We learned that sentinel errors are values of type error you can expose within your go project  by making them public variables. This allows users of your project to compare these values with the errors you return from your projects’ functions, to handle different kinds of errors gracefully.

We then ran into a problem because we wanted to enrich the errors with some additional information for the user, but we wanted to keep the ability to handle different types of errors gracefully.

An example of this could be, to keep with the theme of a configuration provider, showing the user where a syntax error in his configuration has occurred.

Let’s assume your config provider expects the configuration to follow a certain syntax. Using the Sentinel error approach we can create an ErrSyntax sentinel. If we return this to the user, he will know that a Syntax error has occurred, but will gain no further insight.

Let’s add the new Sentinel error to our configprovider package.

var (
  //…
  ErrSyntax = errors.New(“syntax error”)
)

What if we also want to communicate at which line the error has occurred? Naively we could start by  creating and returning a new error.

errors.New(“syntax error in line ” + line)

But now we can no longer compare the error to a known set of variables, and handle different error cases accordingly. 

For this use case the Go standard library provides a couple of useful helper methods for working with errors.

fmt.Errorf can be used to create an error from a format string. It adds another special formatting verb “%w,” which can be used to wrap an error. We can call errors.Unwrap() on a wrapped error, and get back the original error we passed into %w. This means the returned error does not only include the error message of the error passed as an argument but that it can also be handled as needed.

This allows us to add the additional information to our error  for the user like so:

err := fmt.Errorf(“%w: line %d”, ErrSyntax, line)
fmt.Println(err) // “syntax error: line 11”

errors.Unwrap(err) // Returns ErrSyntax.
errors.Is(err, ErrSyntax) // true.

Wrapping an error with the %w verb will create a new error that contains the old error value. This means the error is no longer the same exact value, so we can no longer compare it using the == operator. However, the go errors library introduces the utility method errors.Is which we can use to compare errors. errors.Is will try to unwrap an error if it detects that it is a wrapped error.

err == configProvider.ErrSyntax // false, however
errors.Is(configprovider.ErrSyntax, err) // true

Which means we need to use errors.Is to compare the new errors.

Now we know how to add additional information to a sentinel error value, and we are still able to differentiate between different types of errors to handle them gracefully.

Adding Underlying Error Messages to Your Sentinel Error

What if the message we want to include is not something determined by us, but an error from another package? Perhaps the syntax is parsed in a library we import. 

We may want to use our own Sentinel error value to indicate the general type of error the function has run into, but we do not want to parse (and re-format) the error returned by the library we use. However the error message from the library may include useful information to the user, or for debugging purposes, so we do not want to lose it.

We could also use fmt.Errorf to create an error that wraps the sentinel error defined in the interface, and then add the more detailed implementation-specific error message as a string. To format the underlying error we use %v to format the error message as a string. We could also use the “%+v” verb to format it including field names, or using the verb “%#v” to format it in go object notation.

fmt.Errorf(“%w: %v”, configprovider.ErrSyntax, err)

Then we can still use errors.Is to check for the different error sentinels when handling the error.

errors.Is(fmt.Errorf(“%w: %v”, configprovider.ErrSyntax, err), configprovider.ErrSyntax) // true

This approach is fairly solid and may suffice for your needs, but there are still some downsides.
Perhaps you want to display the error message to the user. But it now includes the string of the implementation-specific error, which contains information you don’t want to show. 

You could use errors.Unwrap to get the sentinel error. If the error originated deep within a call stack, it might have been wrapped again. You could then try to recursively unwrap the error until you no longer can. However, your sentinel error may be a wrapped error as well. It could still contain additional information you want to show to your user, in which case this won’t work.

If you don’t plan on displaying your errors to the user, you can still use this approach to great success.

Suppose you choose to only use this approach on public-facing functions. In that case, you can enforce that calling unwrap will always return the sentinel error. You can also use this approach, but you have to keep in mind the implicit contract your function has to fulfill.

Summary

We learned how to add additional information to sentinel error values by wrapping the error using fmt.Errorf and the “%w” formatting verb, and how to compare them to a known set of errors using the errors.Is function. We learned that this can be used to enrich the error messages for displaying to a user, or for debugging purposes.

When we started to increase the complexity of our hypothetical application, we noticed that we may need to include errors that originated from other APIs or libraries into our error message, but we still want to keep the resulting error messages for the user. This can also be achieved using fmt.Errorf, but may become messy with multiple layers of abstraction. In the next blog post we will describe how golang errors work under the hood, and how you can implement your own error types for maximum flexibility.

</section


Recommend

  • 156

    Error Handling 17 May 2020

  • 95
    • blog.merovius.de 7 years ago
    • Cache

    What even is error handling?

    What even is error handling?January 21, 2018tl;dr: Error handling shouldn’t be about how to best propagate an error value, but how to make it destroy it (or make it irrelevant). To encourage myself to d...

  • 55

    Browsers now have native support for doing asyncronous calls via async/await. This is nice. It is essentially syntax support for promises. How did we end up with this? Well, when adding new features…

  • 52

    In this post, you'll learn how to use exception handling in PHP. As of PHP 5, we can use try catch blocks for error handling—this is a better way to handle exceptions and control the flow of your application. In this arti...

  • 59
    • www.tuicool.com 6 years ago
    • Cache

    C# Futures: Deferred Error Handling

    When writing robust software, there is often a need to perform a series of retriable operations. In order to make the system robust, each operation in the series can be coded so it is independent of the status of the prev...

  • 56

    Errors are values In his post “Errors are values” , Rob Pike, one of the original authors of Go, attends the common perception that one must repetitiv...

  • 63
    • www.tuicool.com 6 years ago
    • Cache

    Things I Enjoy in Rust: Error Handling

    Things I enjoy in Rust: Enums Error Handling Immutability ...

  • 10

    Dec 26, 2012The Neophyte's Guide to Scala Part 6: Error handling with Try Reading time: about 12 minutes When just playing around with a new language, you might get away with simply ignoring the fact that something might go wro...

  • 2
    • developer.squareup.com 3 years ago
    • Cache

    Fsync or Swim Part 1: Error Handling

    Heed fsync errors, especially on AndroidWritten by Bob Lee. Welcome to part one of our series on file-based persistence. We’ll talk a lot about Android, but these lessons...

  • 8
    • blog.anynines.com 2 years ago
    • Cache

    Error Handling in GO – Part 3

    Error Handling in GO – Part 3 Posted by Len...

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK