Note: The solution I'm going to present here is for private functions, a function that are well known to the author of the package; for example, a function that was split out of a larger funtion, in order to have cleaner code.
Functions in golang often returns an erorr value, letting the caller to deciede what to do with them. But there are cases where a binary response is not enough.
For example, let's say my software reads an optional configuration file for a non-critical activity. The implementation function for this activity will be totally OK with the absence of this configuration file. It will just exit with no error. But if the structure of this file is wrong, that something that we want to let the user know, and so this is a real issue. In this case we want to retun error.
So we end up with three possible states: a file is exist and valid and we are going to process it; a file is exist but not valid and we want to return an error; the file does not exist so we don't care, just exiting.
Now, this is a simple example and there are better ways to implement this, but it explains what I'm trying to say.
const configFilePath = "/path/to/config/file"
func DoNonCriticalTask() error {
// exit in case of error
if err := validateConfigFile(configFilePath); err != nil {
// for a processing error, when something is actually not working, return error.
// but if for errors that are valid usage of the program, we just want to exit with
// no error. in this case, the unwrapped eror is nil.
return errors.Unwrap(err)
}
// go on and process the file
...
}
func validateConfigFile(configFilePath string) error {
info, err := os.Stat(configFilePath)
if err != nil {
if os.IsNotExist(err) {
// We don't care. We'll just want to exit.
// The caller eill unwrap this error and will get nil.
return wrapError(nil)
}
return wrapError(err) // this is a real issue, we can't ignore it
}
}
func wrapError(err error) error {
return fmt.Errorf("%w", err)
}