3 ms·
Just 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, d
by LinXitoW 2y ago
Just 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 2y 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 2y 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 2y 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 2y 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 2y 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.
- tuetuopay 2y agoYou always have the option of using a match block if you don't like those chained calls. But I do agree, it's a bit bolted on and kinda ugly. > More terse is not the same as more readable (in fact, I find the reverse). I generally agree, but I also find that "all explicit" also hinders readability because it tends to drown the nitty-gritty details. As always it's a matter of balance :) And I think that neither go nor rust are great in this matter as one is verbose and the other falls in the "keyword soup" with the chain call, the closure, and the format macro. I'm pretty sure something in between could be found.