13 ms·
Go's Error Handling Is Perfect
- nikhizzle 3y agoMuch more in the camp of exhaustive pattern matching such as in Scala or Elixir. Makes my code much cleaner, and I avoid if statements and loops.
- JaDogg 3y agoWhat is with the site - `very good software not virus . ru`
- svacko 3y agoI also found it suspicious, but it seems to be legit - the source of the blogpost can be found on github https://github.com/verygoodsoftwarenotvirus/blog/commit/6a43cc882003479cd023861b11ea39fe299e6c42 https://github.com/verygoodsoftwarenotvirus/blog/commit/6a43...
- lenerdenator 3y ago"You’ll mainly see people complain about having to litter their apps with if err != nil, how they feel it makes the code verbose, and how they miss just writing ten very consequential lines one after the other in more dynamic languages like Python or Javascript." One of the practices you'll see better Python devs use is to wrap stuff in try-catch-except blocks, especially around consequential code.
- thefaux 3y agoSummary: thing people complain about is actually great except when it isn't.
- spenczar5 3y agoPart of the issue is that when people evaluate a language, they do it in a safe setting: a toy problem, a coding challenge, whatever. In those cases, handling error scenarios is annoying and just slows you down. But when the rubber meets the road and you are trying to write code that consistently works, Go’s verbose but simple error system is really an asset. I used to write a lot more Go and have been writing Python, and I miss Go’s style terribly. Errors as values, the build/package tools, gofmt, and the consistent quality of the standard library are what I miss most.
- rob74 3y agoWell, you can simply write "result, _ = function()" to ignore the error for the time being, but that underscore will sit there nagging you to handle the error properly...
- ajross 3y agoThat's true, but it's also true than when people evaluate a problem, they tend to want to imagine the maximally scaled, most robust, "final" version thereof. And so they tend strongly to solve the wrong problem in practice, often in inappropriate languages. Now, for the record, I happen to love Go and agree with the author about its error handling paradigms. That said, I like the python script at the top of the article a lot more still, because the problem being presented is a routine kind of thing that needs to be solved on a routine, ad-hoc basis a zillion times every day. And we don't want people writing the 80 line Go code to do that, we want them doing it in python. Basically exception-style "I don't know what went wrong, just that it failed, let the user sort it out[1]" code is the correct solution to probably 90% of workaday problems we face. You fall through to precise error handling (and/or frameworks like in Rust that force the handling syntactically) only when you need to write code that survives in the absence of a traditional "operator". But most code doesn't run that way! [1] In this case, the problems are likely bad filenames, bad URL, server down, etc... If your developers are running this script themselves, they'll figure that out on their own.
- spenczar5 3y agoTo me, the advantage isn’t so much that you have to handle every error. It’s that you can see, with certainty, which functions and methods are error-free. I even find myself encouraged to write functions that cannot return errors. You don’t get anything similar in exception-based systems. Of course, algebraic error types share this advantage. They are good too.
- conqrr 3y agoGo's error handling is explicit which enforces the need to handle whereas other languages leave it to the developer. More like here's the mess take care of it vs everything looks clean but its really swept all under the rug and can bite you later. With growing dependencies and complexity increasing everyday, I'd rather think of it sooner than later.
- geraneum 3y agoAbout that: res, _ := some_func(…)
- iamcalledrob 3y agoSure, you can do that just as much as you can do "try! some_func()" in other languages. But it's obvious that you're doing something naughty when you do.
- geraneum 3y agoYou’re right. But that’s my point. You can “ignore” the error and Go doesn’t force you to do anything that about it. I’ve seen this in production code. Also I’ve seen people just checking the null-ness of the return value, ignoring the error. And that brings me to my point, which is, it’s all about good programming practices, whether it’s Python or Go. edit: combating the overzealous auto-correct
- iamcalledrob 3y agoAgreed. Go forces you to think about failure just as much as success, and I find that fantastic for building robust software. Errors happen, so you're forced to do something when they do. Errors aren't exceptional. But more importantly to me, Go forces you to think about what a caller to your function might want to know about what went wrong. Errors in modern Go are for communication, not an explosion of "something bad happened, abort!" A long time ago, errors in Go were very basic -- essentially just strings 99% of the time, and I think that's where some of the hate comes from. In the early days, I think it was deserved. But nowadays, with the Go errors package (error chaining), errors are extremely powerful. Good libraries will often return an error structure that provides more context than a stack trace would, e.g. os.LinkError: https://pkg.go.dev/os#LinkError https://pkg.go.dev/os#LinkError tl;dr if you're writing "if err != nil { return err }", you're holding it wrong.
- deleted 3y ago[deleted]
- aeyes 3y agoThe author writes about the Python exception: "Note that for any sufficiently complex program that invokes many dependencies, this stack trace will be so far down the chain that you may not even see where you’re making the call that causes it." I never encountered this problem with Python, not even in a project with more than 1 million lines of code. Are there any real examples where a Python stack trace is truncated in such a way? It almost sounds like his whole reasoning is based on the assumption that he won't be able to understand an error in Python when looking at a stack trace.
- fernandotakai 3y agosame. in fact, with python3.11, stacktraces are absolutely amazing. in his python example, it shows exactly where the error is (down to pointing the function), and the error is quite explicit.
- quickslowdown 3y agoMany levels of try/except nesting could (in theory) lead to this, but my experience is the same as yours, I've never had an issue with a stack trace pushing the relevant data out of the buffer
- CharlieDigital 3y ago> Many levels of try/except nesting could At least to me, this is an anti-pattern in any language that supports try-catch type error handling. Many levels of try-catch should almost always be eliminated except for a small handful of use cases. Unless you 1) can recover, 2) can retry, 3) need to log some local context, 4) need to transform the lower level error (including transforming it for display purposes), my heuristic is that it should always be handled at the highest level possible in the stack and just let the exception pass through.
- quickslowdown 3y agoAgreed! I normally just raise exceptions all the way up until the method that called the function that errored, then handle the exception at the highest level. That way, I just get a stack trace if I don't have any handling. But I'm also an amateur, self taught developer who's probably doing most things wrong :p
- 01HNNWZ0MV43FF 3y ago> The only opposition I have to the Result type in Go is that we wouldn’t be able to make use of it in the standard library without either breaking backwards compatibility, or (breaking backwards compatibility) I'd say then that it's only as perfect as you can be while being compatible with existing Go code.
- peppermint_gum 3y agoMy takeaway from the article is that junior developers are intimidated by stacktraces.
- monknomo 3y agoThe python example stack trace says explicitly what the problem is and somehow it's a problem?
- fireflash38 3y agoDon't you get it? You're supposed to manually create the same stack trace by wrapping your errors! Isn't this much more readable? Error couldn't parse data: couldn't read csv : file not found Manually appending what you're trying to do at each level that checks for errors seems... Error prone.
- monknomo 3y agoI know I'm doing something wrong, but trying to programmatically figure out errors in golang always seems to rely on knowledge of a magic string in the error and I find it frustrating
- raincole 3y agoThis article basically just says two things: 1. Error handling is better than no error handling at all 2. Go is designed to be like Go not Rust Yeah, your typical Go article...
- hamandcheese 3y agoMy main issue with errors in go (last time I used it, which was a while ago) is not any of the things mentioned, it's that the default pattern you learn provides no stack trace whatsoever in the console. There's no error handling in the python code, yet the stack trace will lead you to exactly to where you need to look. To me that is strictly superior when it comes time to debug an error. Edit: maybe not strictly superior, as has been mentioned in replies, wrapped errors with good context at each step does seem like it would be very high SNR.
- reactordev 3y agowhy would you need a stack trace at a panic? You know where the panic is already. 9/10 Go tutorials have you either panic, or write a message to the console. So write a more meaningful message so you know where in the code it's coming from. Stack traces requires instrumentation which can be stripped. I'd prefer not to ship my debug symbols.
- hamandcheese 3y agoAll of that sounds like more work then what you get for 0 effort with a stack trace.
- reactordev 3y agoruntime/debug PrintStack() is 0 effort… what are you going on about? Stack traces are not required, only preferred by some because they don’t program defensively. If you want to include debug information in your binaries, go for it - I’ll be waiting with Ghidra
- hamandcheese 3y agoIn other languages I don't have to explicitly print the stack. So I would not call that 0 effort. I do not care about hiding my debug symbols. Anything I write that has a binary you can touch, you can just go read the source directly.
- bhaney 3y agoHaving an AI generate Python code without error handling, then Go code with error handling (that's also ~6x more code, mostly because of all the verbose error handling) is an absolutely wild argument to use to defend Go's error handling.
- hnrodey 3y agoFalse dichotomy; cart before the horse? My general approach is validate inputs before running "the thing". Check if file exists Parsing CSV file is moved to another class, do more validations on data HTTP calls, if failed, shouldn't eject the entire algo. FWIW, that Go code might be "safer" but it hurt my eyes.
- tombert 3y agoI think it's fallen out of favor, but I always preferred the "Either" monad style of error handling. I think the cool kids call it "railway oriented programming" but I never liked that term. Regardless, it's not radically different than the Go style, just instead of returning back two things and checking for `nil`, it's wrapped up into an algebraic data type. I like this better, because you have the option of very easily doing the "Go style" of checking for success or failure if you want, but you can also exploit the fact that it's a monad and a functor and thus you can just use map and/or bind for all the downstream functions, so you don't have to explicitly check if you don't want to. I feel like this is the best of both world; it still forces you to be explicit (as it's compile-time enforced by the type system), but it gives you nice syntax to make the code cleaner.
- unshavedyak 3y ago> I think it's fallen out of favor, but I always preferred the "Either" monad style of error handling. I think the cool kids call it "railway oriented programming" but I never liked that term. Regardless, it's not radically different than the Go style, just instead of returning back two things and checking for `nil`, it's wrapped up into an algebraic data type. Is that falling out of style? Sounds like Rust's Result type, but i'm unfamiliar with monads/etc. If it is similar, i spent ~5 years in Go and have now had about equal time in Rust - i vastly prefer those algebraic types for errors. Notably because it _can_ be as simple as Go's string concat behavior, but it can also be far more expressive. Yes, Go's type can also be more expressive by adding interfaces/etc, but it's difficult to beat a good enum in my experience. At risk of sounding Rust biased, proper Enums are something that would have made Go more elegant imo. Unlike Generics/etc which are a contentious topic (or used to be, at least), proper enums are just so damn handy and cost so little. My 2c.
- tombert 3y ago> Is that falling out of style? I'm a bit outta the loop in the pure FP world now, because I've been stuck doing Java and C# for the last 5 or so years, but I think that world is trying to move to an algebraic effects-style system. People keep trying to explain to me why that's "better" but I'm not really sold on it, because it just feels like we're reinventing goto, though generally when I have contrary opinions like that it turns out that I'm wrong. I don't really know Rust very well, but I suspect it has a similar way of doing `map` and `bind` to handle downstream stuff, since it has functional roots. The biggest issue I ever had with it in F# is the fact that they don't really have a concept of a monad transformer and therefore you can't easily combine monads, so if you have to mix `async` and the `Choice` monads (more or less F#'s version of Either), you have to explicitly craft your own custom monad. It's really not that bad but it's less sexy than proper monad transformers. Algebraic data types are so utterly useful that I get annoyed that it's not standard in every language.
- huimang 3y agoGo's error handling is good, but rust's error handling is phenomenal. It just doesn't really show in an 18 line program... Rust and Go both force you to acknowledge the possibility of errors in your code, whereas python and ruby, etc, will happily let you ignore them until something blows up. Being able to use `?` in a method that returns `Result<T, Err>` is so elegant and helps readability compared to multiple if err != nil statements in go. In a larger program, ? lets you pass along errors up along the chain until you get to a place where they actually need to be handled, and this lets you consistently deal with them as well. For example, in a slightly more complex example [0], I handle errors on line 162 from monitor_device() (line 176, 181), instead of cluttering up monitor_device() unless I need to. (This was just some prototyping code so please bear with me). [0] https://github.com/andrewzah/fst_rs/blob/master/src/main.rs#L162 https://github.com/andrewzah/fst_rs/blob/master/src/main.rs#...
- cess11 3y agoI prefer switching on Erlang's (Elixir's, &c.) tuples. {:ok, result} {:error, reason}
- reactordev 3y agoI've come to the same conclusion. After teaching several other developers how to Golang, coming from the same general background as me, I found that as we grew in lines of code that Go's error value system was actually really really useful and helped develop more defensive programming styles. Being able to define your own errors and use those instead of instantiating a string every time or having non-standard error string formats. It gets in your way at the beginning but once you get beyond a module or two, it's quite useful, novel, and under appreciated.
- deodar 3y agoLeaving aside the technical merits of Golang's error handling, this article is a straw man. The Python example can be rewritten with error handling. And arguably in this example the Python program is fine. There is no objectively "perfect" approach. I personally prefer getting a stack trace of the point at which the problem occurred. I find that more useful as opposed to getting back an error code which has been propagated and modified through layers of function calls and is often hard to correlate with the original error condition.
- zer00eyz 3y agoI write go. As every programer does you sometimes need to write throw away code. The one off script, a quick tool to clean something up, a loop and a regex to make it cleaner data, aggregating data from two systems into one handy file. Go has this handy way of throwing away an error return. If I have to write something that is trash I skip over the errors. I can ignore missing files or json formatting issues. Granted that code would not pass muster of a code review, it would be apparent what I'm doing. You can cut corners in go, it just reminds you that your doing it.
- deleted 3y ago[deleted]
- aystatic 3y agoI don't necessarily disagree with the overall point, but I'm sad the article misses the big advantage to the sum-type solution. It is not just about "being able to collapse `res, err := func()` into `res := func()?`". With sum types you literally cannot access the Result's inner value without performing some kind of error handling. Even if you just `.unwrap()`. from https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-ride https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-... > The point is, this function signature makes it impossible for us to access an invalid/uninitialized/null Metadata. With a Go function, if you ignore the returned error, you still get the result - most probably a null pointer.
- mrkeen 3y agoI extracted out all the err-checks up to where they're handled. if err != nil { return nil, err if err := json.NewDecoder(response.Body).Decode(&data); err != nil { return nil, err if err != nil { return err if _, err = reader.Read(); err != nil && err.Error() != "EOF" { return err if err != nil { if err.Error() == io.EOF { break } return err if err != nil { fmt.Printf("Failed to fetch data from %s: %v\n", url, err) continue if err := processCSV(csvFile); err != nil { fmt.Printf("Error processing CSV: %v\n", err) Everything just bubbles-up. You've reinvented Exceptions with more boilerplate. Nothing here is 'handled' beyond that. Although I don't read Go, it looks as if main() returns success to its caller even in the failure case. So the Python code "which doesn't handle exceptions" exits in failure properly with a good error message and stack trace. The Go code "which handles all aforementioned errors" returns success for a failed operation, printing a less clear error message than the Python.
- pdimitar 3y agoYou're getting hung up on details of code written by ChatGPT, you do realize that, right? It's a singular line change to return proper OS exit code on error in the main function. And what error message is clearer is not an objective fact but only a matter of preference.
- mrkeen 3y ago> You're getting hung up on details of code written by ChatGPT, you do realize that, right? I'm getting hung up on what the author chose to publish. The author asked, >> do you want to know when something bad can happen in your program, or don’t you? and then published some Python code and some Go code. Of the Go code he wrote: >> Wouldn’t you know it, we have all the aforementioned errors handled!" It's not some subtle gotcha, it's the point of the article and he somehow managed to demonstrate the opposite. It would be like switching from garbage collection to C to make sure you remember to free all your mallocs, then showing demo code that's only a singular line change away from returning all its memory.
- 3y ago
- verandaguy 3y agoI'm not the strongest Go programmer, so maybe my inexperience leads me to still disagree with the ergonomics of "if err != nil" over something like a formal `Result`/`Either` type à la Rust/Haskell/..., but this is a decent bog post. I think using a more structured capital-R Result type, combined with match/case makes for better structured code, and I can't help but think that `err != nil` is a bit of a smell left over from Go's initial decision to avoid doing generics (all of which is alluded to in the writeup). I do disagree with the author using a trivial example to demonstrate that `err != nil` isn't that bad, since most code you'll work with isn't trivial. the errnil pattern obviously isn't enough of a problem to deter many companies, which I've always been interested in, since to me it always seemed like a brittle and awkward error handling mechanism. All that aside, `blog.verygoodsoftwarenotvirus.ru` is an excellent domain name.
- mrsilencedogood 3y agoNo mention of `errors.Is` or error wrapping? IMO that's where the pain starts. I definitely agree that in many programs, when an error happens, you're done, you just blast up the stack and then the error message goes into a sentry report or gets logged etc. But it's also still quite common to want to treat some error conditions as soft errors, or otherwise handle them differently. Now you're doing errors.Is and engaging with the poorly structured world of error wrapping trees. Here's one I hit recently. One of my pet peeves is error messages like "unsupported thing". Ok, what thing was unsupported? Very often people just omit this stuff because it requires you to now cat strings, which is sometimes hard or cumbersome depending on the language. So, I changed my code to have the error be "unsupported thing: <thing>". However, up the call stack, I want to handle ErrUnsupportedThing too. Because maybe there's some reasonable thing I can do to help the user make the thing supported. So I'm calling `errors.Is`. But wait, now I need an `ErrUnsupportedThing` variable to refer to. But each call to fmt.Error creates a new error that isn't `errors.Is`-equivalent to each other. I ended up reconciling this by making `ErrUnsupportedThing` just be "unsupported thing" and then I wrap it by doing fmt.Errorf("%w: %v", ErrUnsupportedThing, thingType). But now what if I want to say "ErrUnsupportedThing: <A sub-error that occurred>"? Well, hope you're using a recent version of go, because "%w: %w" only works in go 1.20 and later. Also the fact that all errors are just "error" instead of being typed errors tends to make people a lot lazier about their error handling in general. In java, if I get a ThingNotSupportedException, I expect to be able to find a getUnsupportedThingType() so that I can explicitly pull it off the string without needing to parse. That's easy in Java, it's a subclass of exception. In go, now you're writing a "subclass" struct to implement error's interface, tacking on extra struct fields and stuff, and since nobody ever actually types their return values more explicitly than `error`, you're now doing casting/duck-typing to pull that information out. Etc. I guess let me put it this way. Go errors are good for 80% of errors and fine for 95%. That's great. But the easy code is already easy! What irks me is when it breaks down when I get to the code that's actually a bit tricky to handle errors for. The type system, the error system, and everything is just like "wow good luck you're on your own". Whereas python/java/etc have my back here.
- fireflash38 3y agoAgreed, 100%. Unfortunately a lot of solutions tend to just be "log the thing". Which now means you have to go log diving to answer a question that the language itself has thrown so many barriers at you to tell you.
- sarahlwalks 3y agoWell, maybe I’m not cool enough to appreciate it, and maybe I’m more accustomed to the style of other languages, but if I see more than a few ifs in a file, it feels like a code smell, and it feels like some refactoring might be good. But I’m hearing that this profusion of error checking “if“ statements is the go way. To me, the ideal in a language is to assume that everything will go right, to enunciate the core intention of the program, function, etc., and to handle errors somewhere else, maybe below the main code or something like that. Mixing it all in right there with the code, even if we end up concluding that this turns out to be the best way to deal with things, seems visually confusing to me when reading the code.
- voidhorse 3y agoEither types are probably the best form of error handling. I would agree that complaints about go's error handling are really silly because said complaints usually come from people that work in languages with exceptions where, if they choose to handle errors, need to handle them in precisely the same way as in go, just in an even more verbose form. The only reason people coming from these languages don't like go is because go actually forces you to either deal with the error or explicitly ignore it rather than just letting your functions propagate failures, which in theory is nice but in practice leads to horrible user experience because the buck wound up not stopping anywhere up the call stack since every caller decided "someone else can handle it".
- marcosdumay 3y agoWait, the Go error handling on that code has the exact same functionality as the Python one. All it does is making the code about 5 times as long and way less extendable. Way, way less extendable. You'll have to completely rewrite the Go code if you want to put it in a library. Now I'm doubting the author discernment on building that opinion.
- joshuamorton 3y ago> do you want to know when something bad can happen in your program, or don’t you? No actually, I want to know when consequential errors that I can be responsible for happen. I don't really care that reading a file could fail (and it can fail in such a variety of ways, there's like 3 dozen distinct errors that could be thrown!) because I can't do anything about that. But if there are errors that I could recover from, that I would care about. Unfortunately while it's possible, very little code distinguishes between error kinds or returns custom errors in go, so your bet is usually parsing error messages, while in Python or rust you have the ability to handle errors that are relevant to you, and let irrelevant ones percolate away. Go on the other hand encourages doing nothing except logging, because it's somewhere between difficult/unidiomatic and impossible (depending on the libraries your building on top of) to handle only certain kinds of errors. As a concrete example, in that csv parsing code, how do you differentiate between a file not found, file too large, and lacking permissions to read it? The stack overflow consensus is that doing that is unidiomatic, and you should just print the error to the user, which means this is just boilerplate for an exception that will ultimately gracefully end processing and print an error/stacktrace to the user. A bunch more typing for precisely the same intent as you get for free in Python or Java.
- fireflash38 3y agoI do like that Python has both typed exceptions and "untyped". So you can classify your exceptions and catch or throw them easily.
- gr__or 3y agoJust as a point of fair comparison, here is their prompt for CSV parsing in Rust (brought to you by Gemini): use std::error::Error; use csv::Reader; use reqwest::Client; use serde_json::Value; #[tokio::main] async fn main() -> Result<(), Box<dyn Error>> { // Define the CSV file path let file_path = "data.csv"; // Open the CSV reader let mut reader = Reader::from_path(file_path)?; // Create a new HTTP client let client = Client::new(); // Iterate over each record in the CSV for record in reader.records() { let record = record?; // Get the value from the first column let url = record[0].to_string(); // Perform GET request let response = client.get(&url).send().await?; // Check for successful response if response.status().is_success() { // Read response body let body = response.text().await?; // Parse JSON let json: Value = serde_json::from_str(&body)?; // Process the parsed JSON (e.g., print it) println!("JSON for URL {}: {}", url, json); } else { eprintln!("Error fetching URL: {}", url); } } Ok(()) } The thing that makes it perfect in Rust, that you can keep the above for a simple script, but if you want to solidify it you just think about how you want to handle the `?`-annotated spots (retries, error-repacking, panics, etc.)
- joshka 3y agoI ran the same prompt through ChatGPT (3.5), and provided the go code to make it give use the equivalent factoring and the rust code is still more readable than go ( use csv::ReaderBuilder; use reqwest::blocking::Client; use serde::Deserialize; use std::error::Error; #[derive(Debug, Deserialize)] struct Data { #[serde(rename = "key")] value: String, } fn fetch_data(url: &str) -> Result<Vec<Data>, Box<dyn Error>> { let response = Client::new().get(url).send()?; let data: Vec<Data> = response.json()?; Ok(data) } fn process_csv(csv_file: &str) -> Result<(), Box<dyn Error>> { let file = std::fs::File::open(csv_file)?; let mut rdr = ReaderBuilder::new().from_reader(file); // Skip the header row rdr.headers().unwrap(); for result in rdr.records() { let record = result?; let url = &record[0]; match fetch_data(url) { Ok(data) => { println!("Data from {}: ", url); for item in data { println!("{}: {}", "key", item.value); } println!(); } Err(e) => eprintln!("Failed to fetch data from {}: {}", url, e), } } Ok(()) } fn main() { let csv_file = "your_csv_file.csv"; if let Err(e) = process_csv(csv_file) { eprintln!("Error processing CSV: {}", e); } } My prompt was: Please write some rust code for me that goes through every row of a CSV file, performs a get request for the value in the first column, and parses the responses as a JSON map of strings to strings. The code should be the rust equivalent of the following go code:... I also added at the end "Use reqwest for the http calls" as chatgpt spat out some code using ureq which was worse than the reqwest version it used without telling it to make the equivalent of the go code. The main difference I see in the two is that as a go developer the language forces you to become intentionally code-blind to the error handling ceremony. `if err != nil` is effectively punctuation, but it's treated the same as your actual program by representing it as tokens. Go treats the tuple(result, err) as a good-enough error handling approach. Rust says here's something equivalent but better.
- amluto 3y ago> I also ascribe that issue to the subject of nil in Go, which is a whole other topic, this article is about errors. I’ll bite. Idiomatic Go code, as in the article, does: response, err := somethingorother() In the error case, response needs to have a value, and nil serves this purpose. If response were a non-nillable type, this would not work. Result-style types solve this problem.
- sirwhinesalot 3y agoGo's error handling is very much not perfect. There's no reason for it not to have a bunch of syntax sugar for extremely common patterns. See all the syntax sugar C# has for handling nullable types. That could easily be done for error handling as well. Or see Odin which has the exact same error handling mechanism as Go but with proper constructs to make it painless.
- erik_seaberg 3y ago> do you want to know when something bad can happen in your program, or don’t you? Almost everything can fail, and we don't need our noses constantly rubbed in it. Knowing how it failed might be interesting, but you're on your own to check for error subtypes and it's rare to define any. 2/3 of the code is way too much boilerplate for delegating error handling when it can be automated more reliably.
- spenczar5 3y agoI liked this article, and the discussion in these comments. I don’t understand why it was flagged. Is there a way to find out? Perhaps @dang summons an explanation?
- joshka 3y agoIn the article > The only opposition I have to the Result type in Go is that we wouldn’t be able to make use of it in the standard library without either breaking backwards compatibility, writing Result variants of existing API calls (so NewRequest, NewRequestWithContext, and NewRequestWithContextAndResult), or issuing new /v2 variants of existing packages (like the recently-released math/rand/v2 package), which then means we’ll have some libraries and programs that use the old style with one return value, some with the new style, and many instances of confused programmers using the wrong one. It would be as close to a Go equivalent of the Python 2/3 transition debacle as I think we could manage. I see Go's approach and Rust's approach as being mostly isomorphic as Go's `tuple(result, err)` maps pretty reasonably onto Rusts's `either(Err(err), Ok(result))`. If Go implemented a language convention that made these 100% equivalent, there would be no real problem except the battle between the old school and new school code authors as to which one is the right one to use in their code. Caveat - I am not a Go programmer, so I have no idea how feasible this is as an addition to the language proper. If you wanted to write Rust like you do go, it would be as simple as the following (cursed): impl From<Result<T, E>> for (Option<T>, Option<E>) { fn from(result: Result<T, E>) -> (Option<T>, Option<E>) { match result { Ok(value) => (Some(value), None), Err(error) => (None, Some(error)), } } } then let (val, err) = someFallibleCall().into(); if val.isSome() // something good with val.unwrap() } else // something bad with err.unwrap() } (noting of course this would be intentionally unidiomatic rust - real code would probably use match (val, err)) for this.