4 ms·
So the author's problem really comes down to "I can't re-order things when I declare them with :=". So don't. Do it like this if you think the function order is
by marcus_holmes 5y ago
So the author's problem really comes down to "I can't re-order things when I declare them with :=". So don't. Do it like this if you think the function order is going to change:
func foo() error {
var err error
err = bar()
if err != nil {
return err
}
err = baz()
if err != nil {
return err
}
return nil
}
And the final "?" proposal doesn't actually deal with the errors. Part of the point of Go's error handling is to force us to deal with the errors. Endless "if err != nil {return err}" statements is kinda an antipattern in Go. Yes it's common, but I'm sure that's because we've been trained by exception handling to just pass the errors up.
A better (but still not perfect) pattern is to wrap the errors:
if err != nil {
return fmt.Errorf("tried to foo the bar with parameter %s, failed with: %w", baz, err)
}
And obviously, an even better pattern is to actually deal with the error - retry after a delay? return a custom "abandon goroutine" error? Endlessly passing the errors up the stack until some function passes an incomprehensible error message to the user is what we're trying to get away from here.
- jesseduffield 5y agousing `var err error` at the top is a sensible solution, I'm surprised this isn't the norm. As for dealing with the errors, there is value in wrapping an error with a message at the source of the error, but much of what follows is just passing the error up to whichever function is responsible for handling it (which may be, as you say, retrying after a period), analogous to rust's `?` operator. There is a proposal for defining a general error handler (see https://go.googlesource.com/proposal/+/master/design/go2draft-error-handling-overview.md https://go.googlesource.com/proposal/+/master/design/go2draf...) but iirc that's in limbo at the moment
- nicoburns 5y agoIME, "retry after a delay" is often much more of an anti-pattern than "pass the error up the stack". That's how you get system's that take an age to do anything before eventually failing anyway (or worse, just leaving you with a perma-spinner). As a user, I'd much rather use a system that fails fast, and allows me to easily retry myself.
- samhw 5y agoThis is how the bank I used to work at ended up with app loads taking almost a minute. People don't intuitively understand the impact of transitivity and exponential growth. It is a very, very bad idea to make a habit of using retry-with-backoff as a default for error handling. (That's before we even consider the semantics of errors in stateful programs, the virtues of 'let it crash', etc.)
- erik_seaberg 5y agoThis. You have to pick a layer that is responsible for retries. If every layer does it, you get a failure amplifier that might become a death star laser.
- samhw 5y agoYup. We once analysed a particularly slow query (as in, a specific call to our server, not a path in the abstract) and saw that across the whole cluster it had resulted in eight million retries. And I think it was a 4xx in the end anyway (not that Go is designed for fancy stuff like telling the difference between two kinds of error!).
- marcus_holmes 5y agoThat's why you wrap errors, so you can unwrap them and work out what caused them. But I totally agree that blindly retrying is a bad practice.
- nicoburns 5y ago8 million! How on earth did that happen?
- samhw 5y agoThe problem is that you don't need to mutate every error at every frame in the stack. I've seen codebases which religiously follow this rule, and most of the `fmt.Errorf` calls don't have much more to say than "error in f(): %s". Languages that implement error handling well do not communicate error states in the form of massive strings like `call to f() failed: g() returned an error with parameter '123': error in h(), with argument x = 'interface{}{}': connection failed`.
- gruez 5y ago>Languages that implement error handling well do not communicate error states in the form of massive strings like ... In other words, exceptions with a stack trace. It's funny how that pattern is being reimplemented because it's not available in go
- marcus_holmes 5y agoNot funny, kinda disastrous ;) The error interface's only member is `func Error() string`. If you want to do fancy things with errors rather than accumulate a massive string then there is absolutely nothing stopping you. As long as you include a `func Error() string` in there too then whatever you implement will be perfectly compatible with the std library and any other Go code that uses errors.
- Andys 5y agoNot available? But that's exactly what Go's panic/recover system does.
- eweise 5y ago"And obviously, an even better pattern is to actually deal with the error" How would you deal with NewFromString? https://pkg.go.dev/github.com/shopspring/decimal#NewFromString https://pkg.go.dev/github.com/shopspring/decimal#NewFromStri... Try different strings? Maybe run through some AI code to figure out what the string really should have been? Most errors you just have to throw your hands up and declare something's wrong. I write in Go and Kotlin and Kotlin has been much less buggy because errors naturally flow up where they are logged and stop the normal execution. I've seen a number of examples in Go where the error was not returned, causing silent failures.
- marcus_holmes 5y agoThere's a ton of static code analysis tools for Go that check whether the error is actually handled/passed/etc - I'd say they're almost mandatory they're so useful :) edit: The thing with NewFromString is that the string you're trying to convert to a decimal can't be. So yes, you're going to have to try a different string if you want this to work. Or you could deal with it a different way - maybe you could see if it converts to a float and then convert the float to a decimal? And in the typical exception-using code base I've seen, there's one try block at the bottom of the stack with a catch block that throws whatever error message it has at the user. Anything is better than this.
- eweise 5y agoI work in the banking industry so I can't just try different strings until it works. These strings might represent real money. If it can't be parsed then something is screwed up. The program is reading the wrong file, the data is corrupt but my point is, there is nothing the program can do to fix it. The program simply needs to throw or log and error and someone needs to research what happened. Here's another example: // NewV4 returns a randomly generated UUID. func NewV4() (UUID, error) { return DefaultGenerator.NewV4() } You want a UUID but it might give you an error. What do you do except also return an error?
- iainmerrick 5y agoThere's a ton of static code analysis tools for Go that check whether the error is actually handled/passed/etc - I'd say they're almost mandatory they're so useful :) I find this slightly at odds with the argument (made by other people, not you) that in Go you’re not reliant on the IDE, because all the program behaviour is immediately visible. I don’t really understand the objection to using an IDE to look up overridden operators and so on. It seems like it’s in the same category as having a type-checking compiler that knows how to look up a type definition and check that you’re using valid operations. There’s no way all the information you need is going to be nearby in the text in any reasonably-sized program.
- azth 5y ago> A better (but still not perfect) pattern is to wrap the errors So manually reinvent exceptions :-)
- marcus_holmes 5y agoWrapping errors is not the same as exceptions. At all. Even vaguely.
- azth 5y agoYou don't get stack traces with wrapping errors, which means exceptions are still strictly superior.
- marcus_holmes 5y agoAgain, you're confusing two different things. You can still wrap error messages and have a stack trace.
- azth 5y agoWhich have to be done manually or through code gen. So we're back at exceptions, but with the downsides of verbosity, and ability to forget to propagate them. Basically inferior exceptions in practically every way.