I find that Go errs way too strongly on the explicit side, but overall it's still better than many other alternatives.
For error handling I tend to write in a style where errors are either asserted out or "folded". If I do several operations in sequence any of them could err, I code in a way where I don't check every single op: instead I make some kind of "error accumulator", or write the code in a style such that if the previous operation failed the next operation will become effectively noop. I then check for errors at the end of the process.
That said, Go is actually right about treating errors as values and not giving special language constructs to throw/catch them.
Rust is one of the few languages I've seen that really does error handling right.
Errors are still values in Rust - usually as part of the `Result` type - but unlike Go, it actually has tools to let you deal with them in a convenient way, like the `?` propagation operator (https://doc.rust-lang.org/book/ch09-02-recoverable-errors-wi...), or the functions on the `Result` type like `map`, `and_then`, `map_err`, or crates like `thiserror` for defining error types, and `anyhow` for easily converting them when you don't care about the details.
The fact that the anyhow/thiserror crates are basically required, or you have to make a bespoke enum for your crate's errors and write conversion functions for them, is not great.
The try operator and Result type is amazing though.
Many languages have this discussion about what functionality belongs in the standard library and what is best left to external libraries - to avoid being stuck with a bad design forever because of backwards compatibility, etc.
I don't see a problem with relying on a few super popular basic libraries for almost every project.
Yea but Go's solution to errors is a straight jacket. There's nothing in say Java that prevents returning a Result type with an error or value and writing code that way.
I guess you can panic/recover in go but it's very very unwieldy and not quite the same.
For error handling I tend to write in a style where errors are either asserted out or "folded". If I do several operations in sequence any of them could err, I code in a way where I don't check every single op: instead I make some kind of "error accumulator", or write the code in a style such that if the previous operation failed the next operation will become effectively noop. I then check for errors at the end of the process.
That said, Go is actually right about treating errors as values and not giving special language constructs to throw/catch them.