Hi Mike, thank you so much for sharing your thoughts and all those resources with concrete examples. Those really help me to understand how errors in go work and how it is different from errors in Java.
Based on what you've shared, here's how I understand it: In essence, because an error in Go is just a value, there’s no built-in way to signal which specific errors might be returned except through documentation. However, since an error is a value, we can also enrich it with additional context like user-friendly messages, stack traces, and more. Plus, sentinel errors are one way to expose API errors and once we exposed those to API users, it is very important to stick to it and not to change. And I found the below article very interesting. Please check it out when you have time. https://dave.cheney.net/2016/04/27/dont-just-check-errors-handle-them-gracefully Thank you again for your sharing. 2025년 2월 4일 화요일 오전 3시 2분 37초 UTC+9에 Mike Schinkel님이 작성: On Feb 3, 2025, at 6:01 AM, Byungjun You <schi...@gmail.com> wrote: In Java, checked exceptions allow developers to define in advance which exceptions a function can throw. However, it seems that Go does not have such a feature. Would it be considered a best practice in Go to document the possible errors that an API function can return? Additionally, what are some best practices that API providers can follow to help API users handle errors effectively? In Go, errors are values[1] meaning most in the Go community frown on try-catch style exception handling as in Java. Each function in Go which can generate an error will typically return that error as an additional value; e.g.: func (d Data) getValue() (value any, err error) { if d.value == nil { return nil, errors.New("value as not been set") } if reflect.ValueOf(d.value).IsZero() { return nil, errors.New("value is empty") } return value,err } Then getValue() would be called like this: value, err := data.getValue() See https://go.dev/play/p/PciRk_t_UaV for full working example. In some cases — though not as many as I would like — Go developers often create sentinel values that might look like this: var ( ErrValueNotSet = errors.New("value as not been set") ErrValueIsEmpty = errors.New("value is empty") ) Then callers can check them using errors.Is() like so: if errors.Is(err, ErrValueNotSet) {...} if errors.Is(err, ErrValueIsEmpty) {...} See https://go.dev/play/p/pamY1pcAoVP for a full working example. A common sentinel error value from the Go std lib is io.EOF[2] However, AFAIK, there is no way programmatically to determine which errors a Go function might return at this time. It would be nice if there were some built-in mechanism for doing that using the reflect package, but I would not expect that to be added as I cannot imagine how it could be implemented without slowing down compilation. Unfortunately. As for best practices, IMO that would be to define sentinel error variables instead of errors created inline with errors.New() or fmt.Errorf() for all errors that your function may return, and then use errors.Join() to join with any errors returned by a function your function called before returning to the caller. Then document your sentinel error variables and commit to not renaming or removing them in future versions of your API. Does that answer your question? -Mike [1] https://go.dev/blog/errors-are-values [2] https://github.com/golang/go/blob/beea7c1ba6a93c2a2991e79936ac4050bae851c4/src/io/io.go#L44 -- 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 visit https://groups.google.com/d/msgid/golang-nuts/f3871600-0bd2-4a91-b94b-f24fa7828a40n%40googlegroups.com.