4 ms·
Go has exceptions. Always has.
by randomdata 2y ago
Go has exceptions. Always has.
- shepherdjerred 2y agoYes, but when writing idiomatic Go one would prefer error types over using panic (panic is Go's term for an exception). The parent is really complaining about idiomatic Go. Which is fair, because idiomatic Go feels like writing Java 6 (that is to say, making lots of compromises due to limitations of the language).
- randomdata 2y ago> panic is Go's term for an exception No. An exception is a datatype. Not completely unlike an error type in concept, but intended for erroneous conditions that could have theoretically been caught at compile time (i.e. the programmer screwed up), as opposed to conditions that are not decidable at compile time (i.e. something happened in the outside world). In Go, panic creates an exception, which may be the source of your confusion. The exception contains any metadata you passed to panic along with other data, like a stack trace. This is not panic in and of itself, though. > but when writing idiomatic Go one would prefer error types over using panic Not really. Idiomatic Go says that errors are in no way special and is faulty to think of them as being special. They are just values like any other. Go exceptions allow attaching any value as metadata. The official line is that panic stack traversal should not cross package boundaries, but internal use is perfectly acceptable. Even the Go standard library does it.
- hot_gril 2y agoThis is confusing, so I'll put it this way, Golang doesn't have exceptions the way someone coming from Javascript, Java, Python, ObjC... would understand: thrown anywhere and automatically propagated up the call stack unless caught.
- randomdata 2y agoThere is nothing to be confused about. The syntax may be slightly different to other languages, but there is no meaningful difference in the design. If you understand exceptions and exception handlers in those other languages, you understand them in Go. There is no exactly a lot you can do with exceptions to see them differ in any big way. Why is it that Go attract so many "experts" who have clearly never used the language even just once?
- hot_gril 2y agoI understand how to use them in Golang, it's just annoying to have to keep checking and returning errors everywhere.
- randomdata 2y agoThe language doesn't impose this upon you. You can use the exception handling system to pass values around, just like you might in the aforementioned languages. Even Go's standard library does it. Read the encoding/json source sometime. You might impose it upon yourself when you start to understand the pitfalls of using exceptional handlers for anything other than exceptions, but such is engineering. Everything comes with tradeoffs.
- hot_gril 2y agoWhat's imposed on a Golang user is two choices, panic/recover or regular error handling, and almost always you take the latter. There's no option of using exceptions the same way you would in most other languages.
- randomdata 2y agoSame as all of those other languages mentioned. What gives you the impression that Go is somehow magically different? Or have I misinterpreted you?
- unbrice 2y agoOne technical challenge to using panic to represent normal exceptions is that most go code is then not exception safe. This was a design choice for the ecosystem if not the language itself (see eg: Effective go about panic not crossing package boundaries, or Google's style guide). Within a package it can work, but then you'll find language support lacking. For example defer is scoped to a function so you need to write pretty unusual code to make the equivalent of a try block.
- shepherdjerred 2y agoLike randomdata said, Go does have this with panic/recover, it's just that Go programmers (from what I have seen) greatly prefer Error types which do not automatically propagate up the stack until caught. Go _doesn't_ have any form of checked exceptions, though, which are required by the compiler to be caught by the caller.
- hot_gril 2y agoGolang programmers indeed don't prefer panic/recover, but that's not comparable to exceptions in other languages.
- tsimionescu 2y agoPanic/recover is perfectly equivalent to try/catch in most languages. There is some small difference related to exactly how un caught panics are handled (in Java, an un caught exception kills the thread that raised it; in Go, it kills the whole Go process); and the amount of built-in type checking for recover vs catch (but you can do manual typechecking and re-panic from a recover if you want). But otherwise they are almost perfectly equivalent. Of course, Go code uses panics much more sparingly than Java or C# or JS or Python use exceptions.
- hot_gril 2y agoIt's different syntax, less convenient, and less obvious what you're try-catching. Also you can't scope it to just part of the function. func outer() { defer catch() { if r := recover(); r != nil { fmt.Println("Recovered:", r) } }() // ... doSomething() // Can panic doSomethingElse() // Can also panic } vs JS: const outer = () => { try { doSomething() } catch(err) { console.log(err) } try { doSomethingElse() } catch(err) { console.error("dang it bobby", err) throw err } } But in any real Golang situation, you'll be working with libraries or teammates who return (type, error) instead of using panics. You might say this isn't inherent to the language, but it kinda is when all the built-in standard libraries like net/http work this way and the official language style guide tells you to do this. func outer() { r, err := doSomething() if err != nil { fmt.Println("Recovered:", r) } r, err := doSomethingElse() if err != nil { fmt.Println("Recovered else:", r) } }
- shepherdjerred 2y agoThank you for taking the time to reply so thoughtfully! So, if I'm understanding you correctly: An exception is an unexpected failure at runtime that could have been caught by the programmer. For example, you might have a switch block with a default block saying "this can never happen". The programmer might miss a case in the switch block leading to an exception. This would be analogous to an unchecked exception (RuntimeException) in Java -- these exceptions are not required to be caught. In Go, `panic` creates an instance of the type `exception`. When `panic` is executed it attaches metadata like the stack trace to the created exception. When I say "exception" this is the behavior that most people think about. An error is for an expected failure, like a file potentially not existing when performing I/O operations. Like you said (and this matches my understanding), error types are just a normal type -- really any type that implements the `Error` interface. There are a lot of utilities functions/libraries that act on that `Error` interface, like assertions for unit tests, the multierr library, etc. `Error` is similar to a checked exception (Exception) in Java, though it misses the behavior you expect like requiring it to be caught, and the try-catch syntax. Additionally, you don't have the stack trace/metadata automatically added. Go programmers seem to _heavily_ prefer errors over exceptions, even when they should be using `panic`. My problem with errors in Go are that they are too easy to ignore accidentally and they don't contain stack traces, so they can be hard to track down. I inherited a codebase where we had _hundreds_ of unchecked errors being silently ignored. I understand that these problems go away if you have linters or a very detail-oriented team, but I unfortunately have no power over the actions of my team before I join. It's also _very_ hard to convince a team that they've been doing things incorrectly by ignoring errors.
- randomdata 2y ago> My problem with errors in Go are that they are too easy to ignore accidentally This is a problem for values of all types, not just errors. One I'm not sure we've figured out how to solve[1]. There are a few languages out there that force variable assignment to try and address the problem, but even then there is really nothing to say that you haven't accidentally ignored the variable assigned. > It's also _very_ hard to convince a team that they've been doing things incorrectly by ignoring errors. To be fair, if you are able to completely leave out entire blocks of logic without anyone noticing even under the most cursory of testing, perhaps it wasn't actually needed? Forgetting entire code branches isn't exactly a subtle bug. [1] Short of going all the way to formal proofs.