📜 ⬆️ ⬇️

We audit bugs

A frequently used contract for functions that return an interface type error value is that the caller should not know in advance about the state of the other values ​​returned by this call without first checking the error.


In most cases, the error values ​​returned by functions must be opaque to the caller. That is, a test in which the error is nil indicates whether the call was successful or unsuccessful, and that is all that needs to be done.



picture from here


A small number of cases usually associated with interaction with the outside world, such as network activity, require the caller to study the nature of the error in order to decide whether it is advisable to repeat the operation.


A common requirement for package authors is to return errors of a known open type so that the caller can use type assert and examine them in detail. I believe that this practice leads to a number of undesirable results:



Confirm the expected behavior, not the type of error


Do not claim that the error value is a specific type, but rather state that the value implements a specific behavior.


This sentence corresponds to the nature of implicit Go interfaces, and is not a [subtype] of the nature of languages ​​based on inheritance. Consider this example:


func isTimeout(err error) bool { type timeout interface { Timeout() bool } te, ok := err.(timeout) return ok && te.Timeout() } 

The caller can use isTimeout () to determine if the error is due to a timeout, by implementing a timeout interface, and then confirm if the error was due to a timeout — all without knowing the type or source. error values.


This method makes it possible to wrap errors, as a rule, with libraries that add explanations to the path of the error; provided that the wrapped error types also implement the error interfaces that they wrap.


This may seem an intractable problem, but in practice there are quite a few generally accepted interface methods, so the Timeout () bool and Temporary () bool will cover a large set of use cases.


Finally


Confirm the expected behavior, not the type of error


For package authors


If your package generates temporary errors, make sure you return the types of errors that implement the appropriate interface methods. If you wrap error values ​​at the output, make sure that your wrappers support the interface (s) with which the original error value is implemented.


For package users


If you need to check the error, use the interfaces to confirm the expected behavior, not the type of error. Do not ask the authors of the packages about open types of errors; Ask them to customize their types to common interfaces by specifying the Timeout () or Temporary () methods, depending on the situation.


about the author


The author of this article, Dave Cheney , is the author of many popular Go packages, for example, https://github.com/pkg/errors and https://github.com/davecheney/httpstat .


From translator


Although the original article dates from the end of 2014, it seems to me that it has not lost its relevance. The described approach is found in few packages, while the rest use the usual approach for errors.


Edits to the text to improve the comprehensibility of the material are welcome!



Source: https://habr.com/ru/post/440818/