3 ms·
As an experienced Go dev, this is literally not a problem. Golang code has a rhythm: you do the thing, you check the error, you do the thing, you check the err
by marcus_holmes 3y ago
As an experienced Go dev, this is literally not a problem.
Golang code has a rhythm: you do the thing, you check the error, you do the thing, you check the error. After a while it becomes automatic and easy to read, like any other syntax/formatting. You notice if the error isn't checked.
Yes, at first it's jarring. But to be honest, the jarring thing is because Go code checks the error every time it does something, not because of the actual "if err != nil" syntax.
- LinXitoW 3y agoJust because you can adapt to verbosity does not make it a good idea. I've gotten used to Javas getter/setter spam, does that make it a good idea? Moreover, don't you think that something like Rusts ? operator wouldn't be a perfect solution for handling the MOST common type of error handling, aka not handling it, just returning it up the stack? val, err := doAThing() if err != nil { return nil, err } VERSUS val := doAThing()?
- foldr 3y agoI personally have mixed feelings about this. I think a shortcut would be nice, but I also think that having a shortcut nudges people towards using short-circuit error handling logic simply because it is quicker to write, rather than really thinking case-by-case about what should happen when an error is returned. In production code it’s often more appropriate to log and then continue, or accumulate a list of errors, or… Go doesn’t syntactically privilege any of these error handling strategies, which I think is a good thing.
- gwd 3y agoThis. Golang's error handling forces you to think about what to do if there's an error Every Single Time. Sometimes `return err` is the right thing to do; but the fact that "return err" is just as "cluttered" as doing something else means there's no real reason to favor `return err` instead of something slightly more useful (such as wrapping the err; e.g., `return fmt.Errorf("Attempting to fob trondle %v: %w", trondle.id, err)`). I'd be very surprised if, in Rust codebases, there's not an implicit bias against wrapping and towards using `?`, just to help keep things "clean"; which has implications not only for debugging, but also for situations where doing something more is required for correctness.
- Mawr 3y agoWell we are in a discussion thread about a language that does just that :) I see two issues with the `?` operator: 1. Most Go code doesn't actually do return nil, err but rather return nil, fmt.Errorf("opening file %s as user %s: %w", file, user, err) that is, the error gets annotated with useful context. What takes less effort to type, `?` or the annotated line above? This could probably be solved by enforcing that a `?` be followed by an annotation: val := doAThing()?("opening file %s as user %s: %w", file, user, err) ...but I'm not sure we're gaining much at that point. 2. A question mark is a single character and therefore can be easy to miss whereas a three line if statement can't. Moreover, because in practice Go code has enforced formatting, you can reliably find every return path from a function by visually scanning the beginning of each line for the return statement. A `?` may very well be hiding 70 columns to the right.
- tuetuopay 3y agoFor the first point, there are two common patterns in rust: 1. Most often found in library code, the error types have the metadata embedded in them so they can nicely be bubbled up the stack. That's where you'll find `do_a_thing().map_err(|e| Error::FileOpenError { file, user, e })?`, or perhaps a whole `match` block. 2. In application code, where matching the actual error is not paramount, but getting good messages to an user is; solutions like anyhow are widely used, and allow to trivially add context to a result: `do_a_thing().context("opening file")?`. Or for formatted contexts (sadly too verbose for my taste): `do_a_thing().with_context(|| format!("opening file {file} as user {user}"))?`. This will automatically carry the whole context stack and print it when the error is stringified. Overall, what I like about this approach is the common case is terse and short and does not hinder readability, and easily gives the option for more details. As for the second point, what I like about _not_ easily seeing all return paths (which are a /\? away in vim anyways), is that special handling stands out way more when reading the file. When all of the sudden you have a match block on a result, you know it's important.
- marcus_holmes 3y agoIt might just be me, but I find both of those to be massively less readable. More terse is not the same as more readable (in fact, I find the reverse). I'm a huge fan of keeping things simple; my experience has shown me that complex things have lots of obscure failure points, while simple things are generally more robust.
- SomeoneFromCA 3y agoActually this is precisely same cadence as in good old C. As someone who writes lots of low-level code, I find Go's cadence very familiar and better than try-catch.