3 ms·
>it’s not easy to wrap fmt.Errorf("... %w", err) >compare errors errors.Is(err, ErrorType)
by c7DJTLrn 4y ago
>it’s not easy to wrap
fmt.Errorf("... %w", err)
>compare errors
errors.Is(err, ErrorType)
- deleted 4y ago[deleted]
- nemothekid 4y agoExcept when you do (1), your "wrapped" error can longer be compared. You end up with the same Rust-style boiler plate of building your own error structs or you eschew the ability to inspect your error types at all. For example, if I have some library that makes a network request and that request times out do I: 1. Return net.Error? 2. fmt.Errorf? 3. My own custom type The problem with (1), is that API is invisible; the caller is now expected to trawl through my code or documentation for any errors I might return that the caller might want to handle. (2) prevents the caller for acting on my error without string mangling (and again has to inspect my code for the exact strings to handle. (3) is the best, but has the same level of boilerplate; and is also, I've found, unidiomatic in Go. The second blight of Go's error handling situation (after if err == nil), is errors are rarely part of an APIs contract; especially outside of the standard library. Often times if you want to handle a special error code for a given API it means actually opening up the source code and hunting down where that error is thrown and hoping that (1) the error type is public or (2) that the library author won't break your string mangling in a later version.
- c7DJTLrn 4y ago>Except when you do (1), your "wrapped" error can longer be compared But that's wrong. When you call String() on it, it will look like the error is just a concatenated string, but it preserves all information about the underlying error, that's the whole point of the %w directive. https://go.dev/play/p/ZUGCKJDs9N5 https://go.dev/play/p/ZUGCKJDs9N5
- nemothekid 4y agoNo; I don't mean the underlying error. I mean the wrapped error. Let's say I'm writing an API client that uses net/http underneath - func CreateUser(username string, password string) error { _, err := client.Get("https://myapi.com"); return fmt.Errorf("Failed to create: %w", err) } Then I create another method like so: func SSOLogin(provider interface{}, token string) error { user, pass, err = provider.login(); if err != nil { return fmt.Errorf("Failed to login: %w", err) } if err := CreateUser(user, pass); err != nil { return fmt.Errorf("Failed to create: %w", err) } } There is no way for the API consumer to discover if the error happened at provider.login() or CreateUser (except for calling String, and Finding "Failed to create". The wrapping is one layer deep, and to go any further you must create your own types. This commonly happens in larger codebases; and what happens is you must define your own error types if you expect your code to be consumed by others. To be clear, there are way to solve this (creating your own types), but I don't think go handles that better than Rust, and the tools that Go does give you push developers in wrapping/concatenating errors into rather opaque types (which is mostly fine for webservers, you can just throw an error 500 and expect the user to try again). But that, plus `if err == nil` makes error handling in Go rather sore IMO. It's easy for developers to write, but not for library users to use, or for later users to maintain.
- the_gipsy 4y agoerrors.Is only works if you have an actual ErrorType which you have to manually hunt down only to find it's a fmt.Errof and it doesn't work.
- c7DJTLrn 4y agoIt's rarely the case that you need to know exactly where the error originated. You just need to check if the error wraps a network error or EOF error for example. If you do need to know specifically what the error was then the API provides public error types.
- the_gipsy 4y agoNo, "the API" doesn't. I have encountered many libs that just do fmt.Errof, and just like that application code does too. So go doesn't do error handling well at all. Give your stdlib a stringly error maker, that's what will creep in everywhere. It doesn't matter if there is a section for creating (somewhat) typed errors - half of go code doesn't bother and together with the poor type system you can't even be sure which type of error you got.
- nindalf 4y agoI wouldn’t bother replying further. Clearly they’ve got a very strong opinion on this. Multiple people have pointed out the issues with their line of thought but they continue to persist. They’ll understand if they reflect on it, maybe.
- the_gipsy 4y agoYea. Actually, they should get hands on with error handling other than go or your run of the mill exceptions.
- IshKebab 4y agoIf you're happy with `Errorf()` in Go then you can just use `anyhow` in Rust. Same trade-off. Easy type-erased error handling. I would recommend it for application code. For library code you should go the extra mile and use proper error types. I think that Go's error handling is actually fine and most people are making a fuss about nothing, but even so Rust's error handling is clearly superior.