Yes, IMO the decision to expose errors should in general be deliberate.
However, there are cases where it makes sense to wrap from the get-go. Two
examples:

• A function takes an io.Reader to parse the stream into some data
structure (think a json-parser). It obviously uses no other input or
abstraction or state. It makes sense to wrap the error - this allows you to
provide structured extra information (like offsets) to the user of the
library and better error messages, while maintaining your users ability to
switch on what actually went wrong.
• OTOH, you might have a library providing abstracted access to files - for
example, a configuration library. In this case, it might make sense to not
wrap the error (until it's proven necessary). That way, you are free to
swap the backend - maybe the config format changes, or data is loaded from
the environment, or some cloud storage. The user doesn't directly know what
the backend is or will be and wrapping errors might cause them to depend on
implementation details that you want to stay flexible on.

The difference between these, I think, is that in second case, you know the
concrete error types and don't want to expose them to the user whereas in
the first case, your *user* is the one who knows the concrete error types
and you shouldn't mess with them.

Note that this distinction was already possible before the new
error-wrapping, by the way. For example, os.PathError
<https://golang.org/pkg/os/#PathError> already wrapped errors to expose the
underlying source, while annotating it with more details. So, the decision
of whether or not to expose wrapped errors has not actually changed that
much. What *has* changed, is that a) you now have a more convenient
middle-ground - if you *want* to expose the underlying error, but you don't
really want to annotate it with structured information, you now have a
convenient way to just write that down without needing to declare an extra
type. And b) it provides a standard way to introspect errors, to save users
from having to type out the entire chain of type-assertions. Basically, it
made the idea of wrapping more usable, for the cases where it does make
sense.

That latter part, BTW, is IMO a blessing and a curse. It's a blessing,
because it can reduce coupling from details in the wrapping case. If foo
wraps a bar.Error which wraps a baz.Error, and then decides that it doesn't
need to use bar and starts directly wrapping a baz.Error, the new way will
just continue to work. In the old way, the type-assertion to a bar.Error
had to be typed out and will now fail. So, by adding this standardized
wrapping and convenience wrappers to walk the chain, implementation details
of foo can remain hidden.
It's also a curse though, because while error-wrapping now provides a way
of subtyping (that's what makes this convenience work in the first place),
this type-system is not checked at compile-time. So if you *where* checking
for bar.Error (and handling that) before, after the switch your code now
breaks, because your error handlers don't trigger anymore - but the
compiler doesn't care.

Sorry if this message is a bit chaotic and stream-of-consciousnes-y :) I
haven't quite figured out how to talk about all of this yet :)
I guess the tl;dr is, that I tend to agree - don't just use %w without
thinking. Make a deliberate choice if you want to commit to this API
detail. And as usually in API design: If in doubt, prefer to start out with
less commitment, flexibility and surface.

On Sun, Mar 22, 2020 at 9:06 PM Adrian Ratnapala <adrian.ratnap...@gmail.com>
wrote:

> Part of the culture of Go is that we are careful to promise as little
> as possible in APIs, as we will be stuck with those promises long into
> the future.
>
> Now with Go 1.13  we can do `fmt.Errorf("It broken: %w")` which means
> the same thing as `fmt.Errorf("It broken: %v")` except callers get
> better ways to inspect the result.  But as the Go blog says, this
> means the use of "%w" becomes a way to expose an API:
>
> > In other words, wrapping an error makes that error part of your API. If
> you don't want to commit to supporting that error as part of your API in
> the future, you shouldn't wrap the error.
>
> Given the preference for *not* introducing APIs, doesn't that mean
> authors should stick to "%v" until they have clear reasons for using
> "%w". After all, it's always possible to switch to "%w" later.
>
>
> --
> Adrian Ratnapala
>
> --
> You received this message because you are subscribed to the Google Groups
> "golang-nuts" group.
> To unsubscribe from this group and stop receiving emails from it, send an
> email to golang-nuts+unsubscr...@googlegroups.com.
> To view this discussion on the web visit
> https://groups.google.com/d/msgid/golang-nuts/CAN%2BHj7jgoMSoyTpcOL%3Da2Rd51MvO%2Bgp0XRzTHjtNZcqPdK8zOg%40mail.gmail.com
> .
>

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAEkBMfG9XtJmCmSY024MGVC%2B9CnyefqS8D678fSX4JYyzjLTgg%40mail.gmail.com.

Reply via email to