Just over a year ago I started this (mostly) daily mailing list, with the goal of going through the entire Go spec, to make it easier for you (and me!) to understand.
So starting today, this list will take on a less structured format. I’ll still be talking about Go. And whenever the spec is updated (as is due to happen any day now, once Go 1.24 is officially released), I’ll revisit the spec to explain what’s new.
But for now, I want to answer a couple reader questsions. I asked for these a few weeks ago, and they’re still welcome. If something bothers you about Go, or just doesn’t make sense, hit that « REPLY » button, and let me know. I’ll try to address it here. And maye also in an upcoming video (I hope to get back into making videos next month, as I should be back into a normal office/studio by then).
For today, the question I want to answer is:
I would like to ask about fmt.Errorf. How is that function able to coerce a string into an error? Is it possible for an end user to type cast a string into an error directly?
To answer this question, the first thing we need is a refresher on what an error in Go even is! Let’s refresh our memory by looking at the relevant section of the spec:
Errors
The predeclared type error is defined as
type error interface { Error() string }
So that’s really it. An error, in Go, is any value which satifies that interface.
So is it possible for an end user to convert a string into an error directly? Yes. It’s quite trivial. All you need to do is create a type that implements that interface:
type MyError string
func (e MyError) Error() string {
return string(e)
}
That’s it!
Now MyError
satisfies the error
interface.
So how does fmt.Errorf
return an error? It does the same exact trick. We can look at the source code to see exactly what it does:
func Errorf(format string, a ...any) error {
/* .. snip .. */
w := &wrapError{msg: s}
w.err, _ = a[p.wrappedErrs[0]].(error)
err = w
/* .. snip .. */
return err
}
There’s much more in that function, but we’re only looking at part of it here, for explanatory purposes. In essence, it reutrns avalue of type *wrapError
. So let’s look at that type:
type wrapError struct {
msg string
err error
}
func (e *wrapError) Error() string {
return e.msg
}
And there you have it. fmt.Errorf
returns a *wrapError
value which implements the error interface by virtue of having an Error() string
method on it.