10 ms·
Go proposal: Leave “if err != nil” alone?
- CalChris 7y agoMods: the title is wrong. It should be Proposal: leave "if err != nil" alone? (Go)
- icholy 7y agoProposal: don't change anything so we can keep complaining about the same things forever.
- madrox 7y agoI've only just started using Go (coming from Python), and I find the existing error pattern cumbersome. It often gets in the way of understanding the function at hand. I think the intention behind the existing pattern is to not over-emphasize the happy path and ensure error conditions are dealt with so programs don't crash, but 99% of the time all I see error handling do is just print the error and then return. There has to be a better way.
- gigatexal 7y agoOnce you get used to it it’s not that bad. Editors make a macro of it so you hardly ever type it out.
- erik_seaberg 7y agoGenerating boilerplate is easier than reading it over and over. We need an IDE than can at least hide it and focus on the 1/4 of the lines that do something.
- convolvatron 7y agoits a bit of a drag. one niggling thing is that if, should it return a zero value along with the nil, means you have to be explicit about which zero value you are using..so refactoring function return types involves touching each of these. not the end of the world, just sad. i do wish there were a type-unified zero value, or that "" and 0 were the same as nil, or some other solution here
- gigatexal 7y agoYeah I have a function like this: return a *struct, err where a nil struct could be the result of a empty result from the database query that was just run or an error if the query had an error. And then the logic of that function is if a job was returned quit early (I’m preventing two things from happening on the DB side). But then there’s an annoying dance of checking if the job is nil and the error is nil that’s the happy path... you can see how this makes the just if the check err is nil usual path welcome
- eweise 7y agoI like how Kotlin and Scala just let the errors bubble up to a level that cares. The code looks very clean and its clear where the error handling is.
- dkarl 7y agoI really like this as well for most applications. You code the happy path and the errors that are part of the product behavior, error handling code is located where it makes sense, and unhandled exceptions get lumped into a catch-all error, such as 500 for an HTTP request. "finally" blocks take care of resources. It really hits a sweet spot for stability and honesty for applications like web services. I get that Go is targeting a systems niche where error handling is more sensitive. I think there will be fewer complaints about Go's error handling when people get a better sense for when to use it.
- marcrosoft 7y ago> all I see error handling do is just print the error and then return. Yeah, that is a problem. At least they had to manually type it out and (maybe) think about how to handle the error during that time. To streamline this is to further exacerbate the issue. They will do a try/catch try/defer `log.Println`. With Python try/catch allows for ultimate laziness. It allows for 1,000 line try/catch blocks that can hide/capture errors that bubble up from anywhere. Of course this is bad development but it's possible. Currently with Go it is impossible to forget about error handling. You have to consciously make a choice on what to do with it. Even if that choice is to log/print it out.
- lgunsch 7y agoI once code-reviewed a serious bug-fix, in Python, causing leaking threads to crash a server. The bug turned out to be a bare try-except hiding a function call with a missing argument; 2 args passed instead of 3. Not exactly a syntax error, but pretty close.
- dec0dedab0de 7y agoMany times I've had to say NO DONT DO THAT! to people writing giant try/except blocks in Python. Usually the answer is to wait for an actual exception and if it's not something you could actually anticipate, then write a tiny try block that just handles the exception. The whole point being that I want it to fail if there is an unknown exception. Which in Go, you wont fail at runtime if you are just printing errors as a habit( I think, I have not done much with go). That said, a lot of times it makes sense to return errors, and perhaps there in lies the difference between an exception and an error.
- superbaconman 7y agoMassive try catch statements introduce excessive indentation, and moves the handling logic away from what's being handled. It's hard to believe that this would be common practice among anyone with a few years under the belt.
- msbarnett 7y ago> Currently with Go it is impossible to forget about error handling. You’re describing some other language that forces you to deal with errors, like Haskell or Rust. A bare io.WriteString(w, “ok”) will happily compile despite returning a completely unhandled error.
- slx26 7y agoyeah, I kinda agree. at least in Ruby, besides the errors bubbling up, if you had to handle the error manually, you could use the truthiness of the language to write just: do_something if err I've read the other proposals, and in fact one of them (catch) is similar to what I do when I'm coding just for myself: value, err := os.Open(filename) exit_on_error(err) where the code for exit_on_error is simply something like: if err != nil { fmt.Println("Something went wrong!") os.Exit(1) } you could make it a bit more robust: value, err := os.Open(filename) handle_error(err, ERROR_FILE_OPEN) where ERROR_FILE_OPEN could be a simple constant, and in the method we have a switch with constants and messages. not a zero cost abstraction, but if you have to handle an error, it probably wasn't a cheap operation either, so the extra call won't be an issue
- basic6 7y agoGo's opinionated, three line if is part of the problem. A single line if or ternary operator would reduce the line noise.
- __david__ 7y agoYeah, it's quite common in C to just do: if (error) return error; It's the same style as go, but so much more compact. If `go fmt` were to render the error boilerplate as if err { return err } …instead of the three line boilerplate it insists on, then it would go a long way. That said, I still support `try()`. It looks good to me, especially coming from Rust.
- deleted 7y ago[deleted]
- NelsonMinar 7y agoLanguage support for exceptions are the better way.
- marcrosoft 7y agoGo is successful and popular because it is different from other languages. Most languages have exceptions and from a Go developer's point of view they are terrible.
- apta 7y agoThis is a non-argument really. Someone can easily say that so many things from a non-golang developer's point of view about golang are badly designed.
- AnimalMuppet 7y agoYes and no. "Go users like it" does not imply "therefore it's good", just as "Non-go users don't like it" does not imply "therefore it's bad". But "Go users like it" does imply "therefore it's not too badly mismatched to what go users are trying to do with it". Likewise, "Non-go users don't like it" implies "therefore it probably isn't a perfect fit for what non-go users are trying to do".
- ramenmeal 7y agoWhat I worry is that this problem is overhyped from new go coders (or people who took a look at go and that's it). The explicit error handling blends into the background so fast. It's never been an issue for me while developing in the past 4 years on go based systems.
- mav3rick 7y agoRust provides really good syntactic sugar that doesn't compromise on catching errors. I get Go is inspired by C, but sometimes it seems too stringent on avoiding any and everything that may make life easier for readers and programmers.
- crimsonalucard 7y agoYou're use to using a hammer on a screw. It's never been an issue for you for four years. It's still a big issue overall.
- xmprt 7y agoYou're exactly the problem the commenter is talking about. I got used to it within a month and while I would appreciate better error handling (if that even exists), I don't think the solution should be rushed and I don't think the way Java handles it is right.
- crimsonalucard 7y agoI agree, but leaving it because people are used to it is not the right solution imo.
- KirinDave 7y agoThis is the real danger of the pattern, isn't it? It fades into the background, lulling your eyes into skipping over what's potentially a major source of bugs.
- tylerFowler 7y ago
- dkarl 7y agoMy experience reading and modifying Go applications has been similar. It often feels like at some point the person writing the code experienced error handling fatigue and all the error cases started blurring together. Every possibility gets explicitly handled, which is impressive, but important common cases that should be a carefully designed part of the system get the same perfunctory treatment as the weird edge cases. Go systems are the only systems I've worked with where silent failures are the most common kind of bug. "But it isn't a silent error; it's in the log and triggered an alert!" Yes, but our customers don't get our alerts. They just get 200s that they don't trust anymore. It makes me think Go's comprehensive error handling can be compared to the performance of C. If you need it, it's great; if you don't need it, the effort you invest in it will detract from something you do need.
- ernst_klim 7y agoIMHO the worst part of Go errors is that they are strings, so if you propagate them, you can't do anything but print them. You can't handle them properly, since you can't match them in a robust way. Just compare how good the error handling is in OCaml: http://keleshev.com/composable-error-handling-in-ocaml http://keleshev.com/composable-error-handling-in-ocaml
- alphabettsy 7y agoHuh? They’re not strings. They’re an interface and you can create your own error type that implements the error interface, one of the requirements being that it has a string output available.
- ernst_klim 7y ago>They’re not strings. They are strings. They are implementation of interface with a single `to_string` method, after you got `error`, you can't say (you could use switch on type, but you should guess which type could it be), so basically you have an opaque object with a single `to_string` method, which is equivalent to string. I can't fancy a more error prone way than that which Go choose. How you match the errors, you match strings? You match types? You don't even know what types could there be, since all what the type system says is that function returns error.
- lgunsch 7y agoI find two of the solutions, the `try` and `catch` to be pretty explicit as well, but a lot easier to read, less gratuitous context. The `expect` I find confusing, and hard to follow. I think it would be confusing enough to cause a lot of bugs.
- notus 7y agoThey are adding generics so why not do something like Rust does (https://learning-rust.github.io/docs/e3.option_and_result.html https://learning-rust.github.io/docs/e3.option_and_result.ht...)
- monocasa 7y agoThat would take adding enums, which they aren't doing.
- akavi 7y agoFor anyone else who was confused: "enums" is what Rust calls sum types.
- stcredzero 7y agoenums/sum type functionality could be implemented using Interfaces in golang.
- akavi 7y agoNo, they can't. At least not in a type-safe way.
- stcredzero 7y agoJust define an Interface which includes the implementation of thisIsMySumTypeAsAnInterface() Then only types which implement that are in that Interface. There's your enum/sum type. Not ironclad, but safe enough to get things done.
- frou_dh 7y agoThe answer to, paraphrasing, "Did you consider doing nothing?" is obviously "Duh, yes". So I don't see this proposal accomplishing anything beyond generating heat on commenter keyboards.
- yamann 7y agoIf Go ever corrects all its mediocre design decisions it would end up being merely a Rust rip-off with GC. Therefore I appreciate that Go designers adamantly stick to their mediocre decisions and never listen to the community.
- kyrra 7y agoThe thing is... the "try" proposal doesn't stop people from using the existing error handling. To me, 'try' is there to simplify the simple cases where you don't need to do anything special except return the 'err' from a function call. There are many ways error handling could be changed that would make the system more complex (such as the "expect" or "catch" proposals he links to). 'try' is to handle the super simple, repetitive case. One more downside I could see is that people will just start using 'try' without thinking about how they should really handle an error, but I feel like that already happens today, so 'try' should just simplify code.
- 40acres 7y agoI've never used Go, but why not treat err like a Boolean? One of my favorite patterns in Python is a simple "if item: do x" where item can be a sequence, a string, or even an integer, where certain values are considered False and certain values are considered True.
- frou_dh 7y agoIn general, they deliberately shunned implicit type conversions (in this case, error/interface → bool. Also notably between numerics). Probably due to being fed up from many years exposure to C's static-but-weakly-typed nature.
- empath75 7y agoI sort of like how brain dead go is, now. It’s not pretty but it’s obvious what the code is doing for the most part.
- joobus 7y agoAgreed. I am not a fan of the Go 2 proposals, generally. I’d rather just use Rust if Go is so intent on reimplementing (poorly) everything missing from Go that Rust already has.
- danielinoa 7y agoSwift has an elegant solution to this called Optional Binding: https://docs.swift.org/swift-book/LanguageGuide/TheBasics.html#ID330 https://docs.swift.org/swift-book/LanguageGuide/TheBasics.ht... Go would benefit from something like this.
- mey 7y agoGo's error handling reminds me of C or PHP error handling. Check the status code. Since everything can mess with that error condition, you have to be careful in how you handle it, but there is nothing forcing you to handle it. I like exceptions because handling an error is default delegated until something wants to or can handle the error. In the case of go, if there is a layer above your function that should process the error, you have to do that if/err in each function that isn't going to handle it. I strongly believe in halting on error as the default response, and unwinding until the correct layer can proceed.
- butteroverflow 7y ago>PHP Oh no, don't even put them in the same sentence. I am no fan of Go, but its error handling approach is beautiful compared to PHP. Every time I call an internal function I have to go through the docs — does it return a NULL, an int, a boolean, or something else? Does 0 signal an error condition, or is it a valid value? Do I have to perform a strict check for NULL/false then? Or is it -1 (see link below)? The situation is generally better with third-party libraries though — they tend to just throw exceptions (if that matches your definition of "better"). https://www.php.net/manual/en/function.openssl-verify.php https://www.php.net/manual/en/function.openssl-verify.php
- mey 7y agoPHP is a bit of hyperbole, but otherwise correct. :) Ruby, Python, Javascript all support exceptions. PHP does support exceptions, but it's core libraries do not use them. Gotta trust PHP to do the worst of both worlds approach. Perl 6 has it but not sure where the world is in the 5 -> 6 migration. I'm not going to count eval(...) as exception handling as a language feature in Perl 5. :)
- chromatic 7y agoI'm not going to count eval(...) as exception handling as a language feature in Perl 5. :) You should. It's a perfectly valid exception handling mechanism. It's unfortunate that the name "eval" is overloaded for two separate behaviors: * catch exceptions thrown as strings or objects * compile and execute code from a string Other than the name, they're different behaviors.
- iainmerrick 7y agoWhy don't they at least allow "if err" rather than "if err != nil", i.e. allow error values to be truth-tested? I don't think I've seen that suggested anywhere, which is surprising to me. I guess it's fear of scope creep, of allowing syntactic shortcuts that might be ambiguous or dangerous in other circumstances? But I would have thought it could be restricted to error types.
- networkimprov 7y agoThis proposal https://github.com/golang/go/issues/32611 https://github.com/golang/go/issues/32611 suggests: on err, <single_statement>
- iainmerrick 7y agoThat’s not the same.
- jammycakes 7y agoThe problem with explicit error handling is that it's all too easy to get it wrong (by forgetting to check the return value) and when it does go wrong, it goes wrong silently, introducing a risk of leaving you with corrupt data. In production. The beauty of exceptions, on the other hand, is that the default option is the safe one. Sure, forgetting to add error handling may leave you presenting a user with a stack trace, but at least you're not billing them for something that never gets delivered.
- stcredzero 7y agoThe problem with explicit error handling is that it's all too easy to get it wrong (by forgetting to check the return value) and when it does go wrong, it goes wrong silently, introducing a risk of leaving you with corrupt data. In production. The problem with exception error handling is that it's all too easy to get it wrong (by forgetting to complete the handle code) and when it does go wrong, it goes wrong silently, introducing a risk of leaving you with corrupt data. In production. In either case, error handling needs to be code reviewed. The best thing to do, is to make the right thing the easiest, minimal friction thing to do. Unfortunately, getting off the "happy path" is often a messy business. My suggestion is to explicitly implement a standard "developer scaffold" to be used when filling in the error handling during development, with penalties for not using it. This makes it easier to find where error handling needs to be fully fleshed out. The beauty of exceptions, on the other hand, is that the default option is the safe one. Sure, forgetting to add error handling may leave you presenting a user with a stack trace, but at least you're not billing them for something that never gets delivered. The entire reason why Unit Testing and Test First made it into Extreme Programming and Agile methods, is that it was way too easy for end-users to see error notifiers and stack traces in production in Smalltalk.
- apta 7y ago> The problem with exception error handling is that it's all too easy to get it wrong (by forgetting to complete the handle code) and when it does go wrong, it goes wrong silently, introducing a risk of leaving you with corrupt data. In production. It's much more difficult to "forget completing the handling code" than it is to overwrite a golang error value and not handle it (which does happen in code bases). The default behavior of unhandled exceptions is to bubble up, unlike golang errors that can get silently dropped quite easily, and I've seen this is in large code bases. Unless you're writing try { ... } catch (..) { /* do nothing */ } in which case you explicitly opt into doing nothing, and for which there are linters that catch this sort of behavior automatically. On the other hand, even the simple func main() { fmt.Println("") } doesn't handle errors.
- djsumdog 7y agoI've found I really like error handling with monads, in languages like Scala. You can have the same boilerplate where you match an Option type on if it is a Some/None or Try type with Success/Error, but you can also chain them together in flatmaps and have one location to return an error. Such things probably wouldn't work in systems languages like Go. I'm surprised we haven't seen more push for functional system/embedded languages.
- andrewjf 7y agoI mean, Rust has exactly those things (Option, Result types), they are iterable, and is a systems language.
- tptacek 7y agoI don't understand why this is on the front page. It's a random Github Issues thread. Unless I'm missing someone, the only Go team member participating is the person telling people not to use Github Issues threads to discuss language changes.
- alanbernstein 7y agoBecause it's relevant to the Go 2 proposal, and someone wanted visibility?
- jrockway 7y agoI am also okay with checking each error. I think errors.Wrap makes for very readable log messages, and still lets you handle different errors differently. The complaint about go programmers silently ignoring errors seems off to me. If you have a function declared like "func foo() (int, error)" and then try to silently ignore the error like "x := foo()", the program won't compile. You have to go in and say "x, _ := foo()" which does look pretty weird. If you do that, you've ignored the error. It's no different than "try { foo() } catch(Exception e) { }" which I guarantee you plenty of programmers do. The resulting faults are their problem; someone tried to help them but they shot themselves in the foot. The real problem is the special case of functions where you want to ignore the value. You can then ignore the error freely. (A good example is fmt.Printf. It returns (n, err). Do you ever look at either? Did you even know that it returns something?) If anything, that's the issue I'd want to fix. Calling fmt.Printf in a "void context" should say "assignment mismatch: 0 variables but Printf returns 2 values". Then the compiler completely prevents you from ignoring errors (and useful results) without making a conscious effort to do so. As for errors themselves, generally I quite like the detail produced by a chain of errors.Wrap. It is more concise and customizable than a full stack trace, but still lets you track down problems. The annoying part is that you can't translate these errors into a less-detailed error for external consumers easily. For example, you might have a chain like "get foo: authenticate: lookup session in database: context deadline exceeded". You want your gRPC service to then return codes.DeadlineExceeded so the client can retry. But you don't REALLY have a good way to convey that programmatically, resulting in ugly code like `if strings.HasSuffix(err.Error(), "deadline exceeded") { return nil, status.Error(codes.DeadlineExceeded, ...) }` which I think is probably prone to error and annoying to write. Checking for errors, though... it doesn't annoy me at all. In 46k lines of code I've written in the last year, "err != nil" is on 1500 of them (500 in tests), and I am super super paranoid about every possible error. It just isn't that big of a deal. "if ...; err != nil" is something you'll probably type if you write go. I don't think it's a plus or a negative. It gets the job done, it's easy to understand, and it empowers the programmer to do the right thing when the program fails. The rest is up to you.
- kstenerud 7y agoI would have liked to see "or return blahblah" type syntax to make it easier on the eyes: f, err := os.Open(filename) or return 0, err Actually, I'll just open a proposal: https://github.com/golang/go/issues/32848 https://github.com/golang/go/issues/32848
- arendtio 7y agoWhat I find most interesting about the whole 'try' discussion: There is one thing that makes the try-function special: It can trigger a return for the calling function. But instead of finding a general way how we can write functions that can do this, we want to add a function that looks like all the other functions but doesn't work like them. So even if I like what we are trying to achieve here, it feels wrong to me.
- cesarb 7y ago> we want to add a function that looks like all the other functions but doesn't work like them. I like how Rust does it: all macros and special built-ins like these have names ending with an exclamation point. So when you see for instance "try!(something())", you know that this "try!()" is not a normal function call.
- lazyjones 7y agoIMHO, they should stop tacking on all those afterthought warts that add a lot of complexity to the language for very little benefit and instead add a simple, more powerful way to let the users implement their own shortcuts, i.e. a macro system.
- wst_ 7y agoThe practice shows that people forget to handle errors quite often and if you are using "golint" it won't even tell you. VSCode doesn't issue a warning, either. I see it all the time in the code that runs on production for few years, now. Thankfully these are not critical issues and, thankfully, haven't manifested yet. But those people are good engineers and seasoned Go developers. If they miss it sometimes, how often it happens in code written by juniors? We are just people after all. Properly designed language should have safety guards everywhere, especially when it comes to errors handling.
- habitue 7y agoStockholm syndrome