|
> if res, err := fn(...); err != nil {
> return nil, err
> } It's bad practice, though unfortunately extremely common, to return unannotated errors like this. I can't think of the last time I've used this stanza. The proper form is, at a minimum, if err != nil {
return nil, fmt.Errorf("executing request: %w", err)
}
Or, if there's additional, caller-actionable information about the error you want to provide, if err != nil {
return nil, OtherError{Inner: err, Extra: xyz, ...}
}
and so on. The point is you have in that stanza the space to program with the error, same as any other value in the function. The... semantic equivalence? which the idiom reinforces is actually extremely good! Error handling isn't any less important than happy path code, and, IMO, language features like `?` suggest that it is.> It's incomprehensible to me that... It is not immediately clear that the first example is incrementing every even number. To get there, we have to parse the method names, recall and parse the special syntax rules for those methods, and, if we're being diligent, reflect on the ownership requirements and allocation effects w.r.t. their parameters, to make sure we're not doing anything with unintended side effects. We're doing basically the same work in the second example, minus the ownership stuff. We're using more characters to do it, but that's not a priori worse. Parsing `res = append(res, v+1)` does not take more time than `map(|a| a + 1)`. Using curly brackets and newlines to demarcate transformation steps instead of monads is not more prone to bugs. It's the same stuff, expressed differently, and, IMO, more coherently: code written in the imperative style is generally easier to understand than functional. (I hope that isn't controversial.) > There's no argument for the go loop being "better" than the Rust equivalent that doesn't also argue that the C version with the additional hassle of bounds-checking and manual incrementing is better still. Reducto ad absurdum. |