13 ms·
Thoughts on the “guard” proposal for Go's error handling
- cratermoon 4y agoI agree with the author mostly. This proposal makes it way too easy to sprinkle panics all over Go code without thinking about the consequences.
- z9znz 4y agoWhat is a Golang person... a Goist? A Goer (hehe, Monty Python). Since I am a goer, I'll use Goist here. I'm not a Goist, but I agree with TFA in general. Don't complicate a language for error handling purposes. My reasoning is that most errors cannot be properly dealt with anyway. Is it the Erlang or the Elixir folks to just say, paraphrased, "let it die"? Just let it blow up. Somewhere upstream can deal with it, because chances are we don't know enough down in the trenches to know what to do when something which we expect to work virtually always, fails. And to improve the noise on the current copy example, I would argue to stop wasting lines!!! There is no good reason to open a bracket, newline, then return err, then close the bracket. It's small and obvious. if err != nil { return err } // put it on one line. It's clean and natural. Edit: Oh now I remember from my limited Go experience :(. They live by their formatter, and their formatter was built in a bondage dungeon.
- nine_k 4y agoThere are errors and exceptions. An exception is something unexpected, unanticipated, exceptional. It's totally fine to crash on it. Running out of memory is a good example. An error is something expected, a less probable but anticipated result. A request timeout is a good example. It's important to have a nice way to process it, and to take an action other than crashing, say, to retry. In this regard, `guard` is a nice proposal: it explicitly shows where we are willing to crash (like `!` in Typescript or Rust or Kotlin), while staying perfectly backwards-compatible and not requiring any rewrites. It's not perfect in other respects though; I liked the `try` proposal better.
- llanowarelves 4y agoThanks for making this distinction. I sometimes have to stop and remind myself that. Can get crazy going up and down the stack, handling errors and exceptions in different ways. It's also very easy in many languages to use exceptions for non-"exceptional" control-flow even though they aren't really meant for that.
- z9znz 4y agoI'm not sure I buy your definitions for errors and exceptions. In practice, many errors are rare cases and are essentially unexpected. They are exceptions in terms of being exceptional results compared to most results. The biggest challenge with error -- or exception -- handling is that most callers are not prepared to deal with something other than success. So the safest thing for them to do is push the failure up, with the expectation that somewhere higher will be equipped to deal with it. If you take TFA's CopyFile as an example, there are plenty of scenarios where copying a file would be part of some complex process. If the file copy fails, it probably indicates either a logic problem (bad path, empty filename, something) or an OS level failure. Either of those cases cannot be dealt with at layers close to the CopyFile call. From TFA example, the guard only eliminates the if err != nil { return err }. This is a bigger problem than can be solved with syntactic sugar. And from my perspective, it is why exceptions exist. If you merrily go on your way in some other languages and try to copy, but that fails, it will generate an exception. If you don't explicitly handle that exception in your calling function, then it will bubble up. If somewhere upstream expects this possibility, it can be designed to respond to a specific type of exception and do something reasonable about it. But your calling function, down here at a low level, will not have any clue what the business logic behavior should be if the CopyFile() fails.
- scatterhead 4y ago> What is a Golang person... a Goist? A Goer (hehe, Monty Python). Since I am a goer, I'll use Goist here. Gopher. > Is it the Erlang or the Elixir folks to just say, paraphrased, "let it die"? That sounds like panic. When the Erlang folks say that, they're talking about letting a process die. The rest of the application is in different processes and doesn't get taken down. So the offending process can be rebooted and operations can continue. > if err != nil { return err } // put it on one line. It's clean and natural. Oh.. erm... well that's explicitly not letting it die. That's returning it and forcing it to be manually "dealt with", as you put it. It seems like you're contradicting yourself. Also, the article explains that returning without context is often a bad idea.
- z9znz 4y ago> Gopher Now I know what being old means. It means you can make references to things, and younger people will completely overlook them. Of course I can do a small bit of internet search to get the answer to my rhetorical question. But I thought (wrongly) that it would be more entertaining to make a Monty Python reference. "Is your wife a goer? Know what I mean?" > that's explicitly not letting it die. That's returning it and forcing it to be manually "dealt with" That is letting it die in the sense of not handling the error locally. It is allowing the failure to go upstream, and either assuming the caller will handle it, blow up, or further pass it up. > Erlang As I understand it, the supervisor processes ensure that the workers are up and running. If one dies, it gets restarted within the constrants of failure count limits and such. Instead of handling errors, you can choose to allow a failure to blow up the "process" (it's not a full OS process, I believe) and be comforted in the knowledge that your process will be restarted. If you look at many business processes (take web app activities for example), you see that failures don't get resolved in perfect tidy fashion. Instead of trying to handle the multitude of possible failure cases, you just accept that many failures cannot be recovered from. You just let it silently die, and you maybe provide a general "sorry, try again" message to the user. That is acceptable in a lot of cases, especially when it is virtually impossible to properly handle all failure cases gracefully. Imagine if a user were uploading an image to a web app, and CopyFile() failed. How would you handle that? Would you tell the user, "Sorry, we got your upload, but we couldn't put it in its proper place, so it failed. Try again."? Or could you let it "blow up" and just tell the user, "upload failed; try again". Now imagine you try to handle failure cases and properly report them all upstream. Now if you write tests, your tests must test for the success path and multitudes of possible failure cases and resolutions. In practice, this just is not done (outside special high risk scenarios like commercial flight and such).
- Jayschwa 4y ago> What is a Golang person Gopher
- z9znz 4y agoI'm sorry. We're all on the internet, and I could have looked it up myself. It was really a rhetorical question as well as an opportunity to enjoy a memory of the Monty Python nudge nudge wink wink skit: https://youtu.be/4Kwh3R0YjuQ?t=25 https://youtu.be/4Kwh3R0YjuQ?t=25
- im-a-baby 4y agoI think it's fine to add syntactic sugar for panic. The standard library already has similar functionality, like regexp.MustCompile. And personally, I've implemented a Must function in many services. E.g. you try to parse a config file during service startup and parsing fails. There is no recourse from such an error. I'm sure I'm not the only one who has implemented such functionality, so why not add it to the language? Regarding the guard keyword, the author's proposal doesn't make sense. The guard call comes after the function call that produces the error, so it's not clear what is being guarded. I guess it's the error being wrapped in the guard message, but explaining that clearly in plain English is quite hard. I agree that context is important however. But maybe the Go team is finally ready to admit that stack traces are more useful than error messages. I'd be fine to know that, for example, an error occurred opening a file and having the language provide a full stack trace of where the error happened. I can do without the custom crafted error messages.
- deleted 4y ago[deleted]
- bogota 4y agoPanic at compile and run time are two very different things. must will panic before you ship your code to production no should want to make panic at runtime look nice
- fuckstick 4y agoYou are gravely mistaken. MustCompile* if it fails panics at runtime. Go doesn’t have a general purpose compile time execution feature. That’s all Must does - is make runtime panic look nice. * MustCompile if you are unfamiliar - the compile refers to the compilation of the regular expression - which is always done at runtime.
- bogota 4y agoAh yeah i was remembering wrong. I normally have this setup as a package level variable and then build and test. But yes its on the test that it’s actually caught
- gregwebs 4y agoI officially proposed this recently and it wasn’t well received: https://github.com/golang/go/issues/56165 https://github.com/golang/go/issues/56165 The proposal the submitted article references has been inactive for years. After submitting proposals and looking at the existing ones and thinking about this more I think the biggest problem with error verbosity is just that you have to also return zero values for the success results when all you want to do is return an error. This obfuscates the intent, making things harder to read and write. In some scenarios such as adding an additional return result it forces changes on multiple unrelated lines when it should just be a single-line change. It's also possible that errors wouldn't be as verbose if Golang had automatic error tracing like Zig (a slightly better form of stack traces)- in that case the need for error annotations would decrease. With that and zero values removed, just allowing the error return on a single line instead of 3 might be all that is needed. if err != nil { return err }
- pornel 4y agoSingle line reduces noise, but doesn't make it less cumbersome. My pet peeve is that when error types differ, I need to juggle between `err` and `err2`. When I refactor code and add/remove fallible functions, I need to change between `=` and `:=` accordingly only because the error has to get a named variable every time.
- im-a-baby 4y agoGenerally, returning a typed error in the function signature in Go is very annoying and dangerous. An example why: import "fmt" type MyError struct{} func (m *MyError) Error() string { return "" } func myFunc() *MyError { return nil } func main() { var err error err = myFunc() if err == nil { fmt.Println("No Error") } else { fmt.Println("Error") // This prints } }
- svnpenn 4y agothats kind of misleading, as you could just do: func myFunc() error
- deleted 4y ago[deleted]
- divan 4y agoI like the second snippet (The actual CopyFile function). It's verbose at the right level – from the single glance I know what will happen in all possible execution paths. No additional cognitive load on figuring out what's going on behind the scenes on errors. Priceless.
- akira2501 4y agoThe call to "Guardf" isn't great, and the fact you have to call it at sites that use it regardless if an error was generated or not offers the opportunity for plenty of "behind the scenes" subterfuge. It's all an incredible amount of effort to avoid what boils down to a basic nil pointer check. I agree the blocks are unsightly, but they're as plain and flat as you could possibly hope for. I almost think you'd be better off using syntax highlighting to obscure them if they're that painful to have to look at.
- fibonachos 4y agoI have found the code folding in GoLand to make this basically a non issue (for me personally). if err != nil { return nil } becomes if err != nil: err <up arrow>
- divan 4y agoExactly. So much magic and complexity just to perform the nil check behind the scenes. Also it has a bit of educational component. It's like a seatbelt. In some societies people consider seatbealts annoying and irritating thing that restrict their freedom. They use seat belt plugs to get rid of that annoyance. Go just forces you to use it, regardless of your initial predisposition to the seatbelts.
- jasonhansel 4y agoI like that the execution paths are visible. The issue is that the code's actual functionality gets hidden behind all the noise created by error handling.
- divan 4y ago
- marcrosoft 4y agoI feel like the language is good as is and is constantly being attacked to become a crappier version of itself.
- vbezhenar 4y agoHonestly all I need is Java exceptions without checked exceptions nonsense. Just let me throw exception, collect entire stacktrace, let me catch exception whenever I want or dump stacktrace if nobody caught it and goroutine died. It's simple and it works. Investigate recent Java improvements with stacktrace logging (I think Spring Boot does that, basically it turns stack trace backwards as error cause often is on the last part of stacktrace) and implement as necessary. I love golang as much as I hate its error handling. If not for it, I'd switch to it tomorrow. As it is now, I'd rather bend Java to be golang. Just because of error handling. The only thing that I'd add to Java exceptions is to enrich the exception stacktrace with all serializable local variables on every level (including parameters). Yes, that's a lot of data and some smart approach should be taken to serialize it to string. Basically all "simple" local variables should be included to stacktrace. That'd be another level of error reporting.
- jherico 4y agoGo and Rust both strike me as languages made by people who HATE Java/C++ style exception handling but thought the best way to deal with it was to invent a language that constantly punches you in the face. the fact that the most popular Go IDE actually has logic to automatically fold and hide the constant error handling boilerplate and that people seem to have no problem with that makes me feel like I'm taking crazy pills.
- vore 4y agoI feel like Rust isn't too bad at it: if you want the almost C++/Java level of wrist strain just use anyhow::Error, remember to put ? after fallible calls and downcast to extract the underlying error. C++ and Java exceptions have non-local flow and it's hard to determine what function can throw what, so if you do want to add error handling cases you would have to inspect every function to know if it throws or not. Yeah, there's noexcept and checked exceptions but one was a late addition that legacy codebases don't use and the other everyone just does throw new RuntimeException(err) anyway.
- typical_gopher 4y ago
- admax88qqq 4y agoGo's error handling is one of the worst laguage decisions ever. Exceptions are emuch cleaner and more sensible to handle. If you're worried about not handling erorrs turn on a check to force all exceptions to be handled. Writing boilerplate error handling code for every function call is one of those things that can make you _think_ you're bring productive because you're writing a lot of code, when in reality it's mostly just tedious mechanical work of little value. Swift does an okay job at this. Java forces you to handle _most_ but not all exceptions.
- typical_gopher 4y agoMy only complaint about go error handling is that they are not encoded as unions/sum types/variants/choice types (whatever you want to call them), so in practice this leaves us the chore of checking and passing zero values and nils everywhere. Sugar for error handling is already a solved problem (.e.g: Rust, Swift, or Zig). Go will be damned by the community's NIH syndrome.
- klabb3 4y agoAs someone who likes Go, agreed 100%. Sum types + pattern matching + tuples alone are so powerful, they could fix like top 5 of my biggest annoyances with Go. Add some syntax sugar (like '?' from Rust), and it fixes a majority of the boilerplate issues. Imo, people praise Rust too much for the ownership and safety story, and way to little for its (more simplistic) excellent decisions around simple types.
- baq 4y agoA bit of a tangent here but a similar thing happened with Python 2->3: people focus on Unicode (especially native English speakers) and completely forget that exceptions were made unbroken.
- sidlls 4y agoYup. Go desperately needs a sum type with compiler-enforced matching on all possible cases. And errors need to be such a type. Long before Rust I used to implement this (to the extent possible) with templates in C++ when I'd start work on a new code base, if it didn't exist. With generics added to Go I might start the same at my current role. It's too bad there aren't other tools to support it (e.g. macros).
- SPBS 4y agoI don’t mind Go’s error handling a single bit. Reducing 3 lines to 1 line is hardly a win because people will still complain that their code looks bloated. I’m not against more concise error handling I just don’t like any of the current solutions, and the status quo may still be the best.
- teeray 4y agoOkay friends, the reason why Go’s errors are good as-is is because you actually have to think about them. That is a feature. If you stop thinking about them and live in happy-path land forever, you will forget that bad things can (and often do) happen during the execution of your code. Even if you just return the error up the call stack, you’re still making a decision about it. You have an opportunity to examine the error at that point and even wrap it with additional context based on what you know at that point in the call stack. The exceptional path of the code is constantly maintained instead of handed to a runtime with the hopes it will do the right thing (particularly with things like retries).
- 015a 4y agoThat's fair, but I don't feel that Go's position of biasing to the absolute opposite end of the spectrum is reasonable. Ultimately; in a typical app; if you've got a phrase of code like: result, err := db.GetUser("12345") if err != nil { return nil, fmt.Errorf("failed to query user: %v", err) } There's an inherent asymmetry between how often those three conditional lines are executed relative to the undeniable fact that error handling is three times the code line volume versus the core business logic. That's not ok; and when I look at the argument phrased like that I find it hard to believe that anyone reasonable can take the stance that its desirable that error handling requires writing substantially more code than The Thing Which Generated The Error In The First Place.
- svnpenn 4y ago> There's an inherent asymmetry No there's not. People seem to just want to ignore all errors, and let the language "figure it out", AKA exceptions. If you want to ignore errors, then just go all the way and do that: result, _ := db.GetUser("12345") then, you never have to worry about errors, you can just code in bliss and wait for the "panic" because you didn't want to bother with proper error handling.
- peterashford 4y agoThat really misrepresents how exceptions are used, IMO.
- throwaway183019 4y agoI don't think the article's `CopyFile` function is a good example for either `guard` statements or exceptions. We can come up with better examples (feel free), but I'm going to focus on that since it's the one used in the article. As a library consumer, I would like to know if the error happened in the source file or the destination file, ideally without requiring me to know implementation details of the function, so I don't have to worry if the implementation changes and now my `if errors.Is(err, fs.PathError)` no longer works and instead became a bug. For example, the caller might want to: * Show a user-friendly message to the user if the problem is in the source file. * Return gracefully if there's not enough space to create the destination file (e.g. "copy as many files as you can to this drive, and tell me which ones you copied"). * "Bubble-up" the error in any other situation. A function that lets me know which file was the cause of the problem might look like this: type ErrSourceFile struct{ error } type ErrDestFile struct{ error } func CopyFile(src, dst string) error { r, err := os.Open(src) if err != nil { return &ErrSourceFile{error: err} } defer r.Close() w, err := os.Create(dst) if err != nil { return &ErrDestFile{error: err} } defer w.Close() if _, err := io.Copy(w, r); err != nil { return err } if err := w.Close(); err != nil { return &ErrDestFile{error: err} } return nil } Since now errors are actually useful to the caller function, the `guard` statements became unnecessary everywhere except in the `guard io.Copy(w, r)` case, because now we must handle nils (though I guess with generics we can create a wrapper that allows doing stuff like `guard WrapIfNonNil[ErrSourceFile](err)`, but whether that's better is debatable). If we tried to do this with exceptions so that we can actually catch them, the code would become: type SourceFileException exception type DestFileException exception func CopyFile(src, dst string) throws error { // Now necessary because the `try` blocks introduce new scopes. var ( r io.ReadCloser w io.WriteCloser ) try { r = os.Open(src) } catch err { // Wrap exception because we don't want to hide // the inner exception. throw SourceFileException(err) } defer r.Close() try { w = os.Create(dst) } catch err { throw DestFileException(err) } defer w.Close() io.Copy(w, r) // The caller might want to check the hash to decide if it // should delete the file or not. try { w.Close() } catch err { throw DestFileException(err) } } ... which is even more cumbersome than plain `if` blocks. Exceptions have their advantages in certain cases, but they also have their downsides, it all depends on the situation and what you want to do. As everything, it's a trade-off.
- svnpenn 4y agoI really wish people would stop trying to "fix" Go error handling. Go error handling is fine. If you want a terse language, use something else. Go error semantics have been this way for over a decade, they aren't going to (and shouldn't) change. The whole point of the current syntax is so that YOU DON'T IGNORE ERRORS. People keeps saying, "BUT MY LOC", just simply don't get it. You're supposed to do something with the error, either return it up the stack, or decorate it, or log it. Go didn't choose the exception route, stop trying to bolt exceptions on, and just adapt or stop using Go.
- ownagefool 4y agoAdmittedly I'm probably just not the best programmer in the world, but I just copy and paste something like this: if err := c.ShouldBindHeader(&headers); err != nil { log.Println(err.Error()) c.JSON(http.StatusUnauthorized, gin.H{"error": err.Error()}) return } I don't think I give any more or less thought about it overall. Honestly, I'd probably rather just write the happy path, but still like the language either way.
- the_gipsy 4y agoIn my experience, errors still get ignored - by accident. The err var juggling sometimes can't even be checked by a linter. And of course it's just EXTREMELY verbose. Compare to rust, where it's not verbose at all, AND the type system is sufficient to force you to handle errors.
- _ZeD_ 4y agoFrom TFA >>> When working in try/catch languages like JavaScript, I often easily forget which functions can throw. Even if I do remember, it’s easy to think “I think this gets caught somewhere up the call chain”. ...yeah, because what we really need are fckn CHECKED EXCEPION I really never understood the gate for them. Exceptions can be thrown from a function? Then should be parti of the signature!!! There are a lot of exceptions that can happen? Good, then keep track of them! Or handle them, or wrap them. With CHECKED exceptions there is no way to work only on the happy path
- masklinn 4y ago> I really never understood the gate for them. The one major implementation so far has been unwieldy and awful, and I’m not sure anyone has come up with a version which sucked any less. Furthermore when you provide both checked and unchecked exceptions, it’s a lot smaller a step to switch from checked to unchecked than it is to switch from return errors to panics, and the “wrongness” is a lot less noticeable.
- baq 4y agoUntil you change the function signature two libraries deep and break all code depending on it. It could work with only one exception type, incidentally go encourages this.
- petesergeant 4y ago> I prefer how Go forces me to think about errors at every turn Does it though, or does it just cause you to boilerplate `if err != nil {return err}` over and over? In the example given, the author is simply manually rethrowing up the call chain to the layer that can handle it, it just takes three extra lines to do it, every single time
- svnpenn 4y agoI agree, LoC is a very important measure of a language, not flexibility. If you really want to, just ignore the errors with "_" and let the runtime panic. Or maybe just use the language as intended.
- deleted 4y ago[deleted]
- masklinn 4y ago> LoC is a very important measure of a language, not flexibility. That is a completely false dichotomy. You can provide terseness for the common case while allowing flexibility for when that’s warranted. You can even artificially increase verbosity as disincentive for constructs which are necessary but should be avoided.
- grey-area 4y agoI agree it’s be nice to be able to return error + zero values in a simpler way. Personally I think they should add a keyword like returnif or check to do this, as sometimes (maybe half the time?) you don’t need to decorate an error if you want to handle it higher up. So you could just do: returnif err I hate the guard proposal as it mixes up error handling and function calls, but it’d be nice to have a simpler less verbose way to return errors. In comparison with languages throwing exceptions though, Go is far far easier to reason about, even if more verbose.
- kitd 4y ago
- baby 4y agoShouldn't go get rid of nil, perhaps by introducing sum types, before even thinking about error handling? I feel like that should be the priority personally
- sidlls 4y agoThe two are married in my mind. Proper error handling with something that doesn't make my wrists ache just thinking about it falls out handily when combined with sum types.
- riwsky 4y agoMy proposal: add “→”, with the following semantics: Instead of having programmers slog through an if statement: if err != nil { return err } Allow them to instead write [the superior]: err != nil → { return err } PROS: * composes well with go’s existing “errors as values” philosophy * matches →‘s usage in formal logic to signify implication * a whopping 50% less verbose than “if” CONS: * to make it backwards compatible, we’d need to also add “←”
- kaba0 4y agoThis seems like rust’s return types with `if let`, which is a general concept instead of hard coding it for one specific use case.
- topspin 4y agoHere is the same "actual CopyFile function" using a fictional Go Result type and a little sugar, which is what Go should be doing: func CopyFile(src, dst string) Result<> { r := os.Open(src)? defer r.Close()? w := os.Create(dst)? defer w.Close()? io.Copy(w, r)? } Yes I know this isn't likely to be the precise syntax; it's pseudocode for illustration only. Result<> would imply an "error", a miniscule adaptation for a working programmer. Every case of err != nil has to be "thought about" or it won't compile. Nothing is ignored. Explicitness hasn't been reduced one meaningful iota. All the error handling branch noise is gone. Retrofitting this onto Go is obviously not trivial. Obviously this won't work for all conceivable Go constructs. However, it will work almost all the time and immediately solve one of the worst aspects of Go. Some of the claims in this thread are really frustrating. First, just because some people have a problem with the incessant "if (err != nil) { return err }" plaguing all Go code doesn't mean they want to "just ignore errors." They want a better solution. Second, the frequency of "if (err != nil) { return err }" does not indicate some failure on the part of the developer that they need to "fix." There is no fix in the prevailing Go language other than obfuscating code behind ever greater abstractions, the exact opposite of the intent of the Go programming language.
- deleted 4y ago[deleted]
- tgsovlerkhgsel 4y agoWhat's the benefit of the result type here, instead of keeping the "error" in the last spot? This would require refactoring everything, leaving you with "old style" and "new style" APIs. Couldn't "?" just mean "strip the error", i.e. act as if the function only returns the value(s) it returns, and if the "?" "triggers", it would just return default/empty values for all parameters + the error in the error field. This also allows seamlessly mixing the abbreviated version and the full current approach if you need it for some reason. I honestly also don't see how retrofitting it wouldn't be trivial. One could probably even write a preprocessor for that. The most common argument I've heard is that it will lead to less explicit error messages because people will just pass through the original error instead of f, err := os.Open(savegamepath) if (err != nil) { return nil, fmt.Errorf("Couldn't load savegame: %s", err) } so providing some convenient way to add this information would be great. Especially if it could be added once per function, and the whole system would preserve the stack trace like exceptions in any sane language do. Just adding the stack trace alone will make the additional context a lot less important. I'd rather have human-written context and the stacktrace, but if I can have only one, I'd rather take the stack trace, especially since humans will be lazy and often omit the context.
- silisili 4y agoAll of this seems to be treating errors as nuisances. Go was written to treat errors as values. As someone who has written Go code professionally for more than a decade, I agree with that mantra and error checking is not really a pain point. I don't understand all the proposals that claim that it is...
- alkonaut 4y agoThe article as well as several comments repeat that “Go forces you to think about the error”. But isn’t that the opposite of what the tuple return pattern does? It allows you to ignore any error you want? It’s just like an integer error code in C or bash, you check it when you remember but no one stops you from using the value even in the error case. If the result and error was a single value (which I assume is the alternative here - not some Java-style exception system) then you’d be forced to consider the error. The best you can do with a r, e tuple is checking if e isn’t nil over and over possibly cheered on by a linter?
- Someone 4y agoIf I were in charge of go, I would add some compiler magic and allow foo := bar() onError(err) { return nul, errors.New(“bar failed”, err) } as (almost; difference would be the scope of ‘err’) shorthand for foo, err := bar() if err ≠ nil { return nil, errors.New(“bar failed”, err) } I also would allow a short-hand version for single statement blocks: foo := bar() onError(err) return nil, errors.New(“bar failed”, err) That, IMO, keeps the spirit of (relative to, for example, JVM stack traces and try/catch) being in control of error reporting at the price of having to do more work getting decent partial stack trace-like information out of errors, but also removes a few pain points: - it’s shorter (especially the single-line version) - it limits the lifetime of ‘err’ to the ‘onError’ statement. It also, IMO, reads easier than these proposals. An implicitly declared ‘err’, as in foo := bar() onError return nil, errors.New(“bar failed”, err) would be even shorter, but IMO would be a bit too magical for go, even if we made it clear magic is happening by giving that variable a special name, as shell does with ‘$?’.
- lamontcg 4y agoAm I the only person in the world left who remembers and vastly prefers Erlang's go-ahead-and-just-crash model?
- philosopher1234 4y agoThe point of go hate always has been and will continue to be as a way of avoiding doing your job. Hating go is giving you an excuse to twiddle your thumbs and fantasize about impractical solutions (Rust)