To help other ppls like me (i had a hard time with this, as a Go newbie), i answer my own question here:
It seems fmt.Println(topLevelErr) NOT unwraps all the errors and prints all that errors (by calling .Error() of every unwrapped error) with some ": " as delimiter between the errors, as i thought initially. This is NOT what happens.
Instead all the go-lib errors do this by themselfes, by implementing the error interface in this way: Their Error() method (error implementation) tests if they have some wrapped error inside of themselfes and if so, print it "chained". This means they print their own error msg + ": " + the wrapped error´s msg.
You can see this, if you have a look at the Go source code, as VonC mentioned below.
I personally would call this a rather stupid design. And totally unnecessary too, since all errors are wrapped anyway. Therefore all errors could be unwrapped manually, via errors.Unwrap() method from go-lib. When unwrapped, we (or also fmt.Println()) could print the error msg of every error, together with some delimiting ": " stuff. This would be a way better design, than "every error is wrapping the msg of it´s inner error inside it´s own msg". I think that´s the reason why it confused me a lot. :)
Also i advice you to do the same thing as i did in sample code below: At least let `fmt.Errorf()` create that wrapped error msg, instead of putting some `": "` string into it, by yourself. So it is granted, when the wrapped error behaviour changes, yours do also.
Previous advice was incorrect, so i removed it. I accidentally thought fmt.Errorf() adds a ":" to the text, when wrapping the error with %w. This is not true. As VonC already pointed out: Most stuff in the Go standard library do this in the implementation of their error interface:
return "own error text" + ": " + e.err.Error()
So it doesnt matter, which of these you use, in your error implementation:
return fmt.Errorf("%s: %w", "own error text", e.err).Error()
return "own error text" + ": " + e.err.Error()
In both cases, you need to do the ": " thingy by your own. In short: The fmt.Errorf() function does NOT automatically put the ": " into the created error´s msg.
This shows, what´s all about:
type CustomError struct {
Msg string
Err error
}
func (e *CustomError) Error() string {
if e.Err != nil {
// Added a ":" here, because fmt.Errorf() does NOT
// automatically do this (as i accidentally thought above)!
wrappedError := fmt.Errorf("%s: %w", e.Msg, e.Err)
wrappedErrorMsg := wrappedError.Error()
return wrappedErrorMsg
// Also you can do this instead (like most go-lib pkgs):
// return e.Msg + ": " + e.Err.Error()
}
return e.Msg
}
func (e *CustomError) Unwrap() error {
return e.Err
}
func printAllWrappedErrors(topLevelError error) {
fmt.Println(topLevelError)
}
func printCustomErrorOnly(topLevelError error) {
var e *CustomError
if errors.As(topLevelError, &e) {
fmt.Println(e.Msg) // <-- This is the difference
}
}
func printCustomErrorIncludingAllWrappedErrors(topLevelError error) {
var e *CustomError
if errors.As(topLevelError, &e) {
fmt.Println(e) // <-- This is the difference
}
}
For a better understanding just play around with the sample.
You can find it here on GitHub.
And big THX to VonC for the prompt help and the Kickoff! Was very helpful! :)
Have fun.