I am reading The Go Programming Language book and in it's description of the error package and the interface
package errors
type error interface {
Error() string
}
func New(text string) error { return &errorString{text} }
type errorString struct { text string }
func (e *errorString) Error() string { return e.text }
it says
The underlying type of errorString is a struct, not a string, to protect its representation from inadvertent (or premeditated) updates.
What does this mean? Wouldn't the package hide the underlying type since errorString
isn't exported?
Update
Here is the test code I used implementing errorString
using a string
instead. Note that when try to use it from another package, you can't just assign a string as an error.
package testerr
type Error interface {
Error() string
}
func New(text string) Error {
return errorString(text)
}
type errorString string
func (e errorString) Error() string { return string(e) }
And testing it with the suggested codes
func main() {
err := errors.New("foo")
err = "bar"
fmt.Prinln(err)
}
Will end up producing an error when compiling
cannot use "bar" (type string) as type testerr.Error in assignment:
string does not implement testerr.Error (missing Error method)
Of course there is a downside to this since different errors that happen to have the same error string will evaluate to being equal which we don't want.
Errors can be returned as nil , and in fact, it's the default, or “zero”, value of on error in Go. This is important since checking if err != nil is the idiomatic way to determine if an error was encountered (replacing the try / catch statements you may be familiar with in other programming languages).
Consider the following code: func main() { if err := doSomething(); err != nil { // log here and exit? } } func doSomething() { f, err := os.
error is a built-in interface type in Go. An error variable represents any value that can describe itself as a string . The interface consists of a single function, Error() , that returns a string error message.
Error handling in Golang is done through the built-in interface type, error . It's zero value is nil ; so, if it returns nil , that means that there were no errors in the program.
The book's explanation about "protecting representation from inadvertent updates" looks misleading to me. Whether errorString
is a struct or a string, the error message is still a string and a string is immutable by specification.
This isn't a debate about uniqueness either. For example, errors.New("EOF") == io.EOF
evaluates to false
, although both errors have the exact same underlying message. The same would apply even if errorString
was a string, as long as errors.New
would return a pointer to it (see my example.)
You could say a struct implementing error
is idiomatic since that's also how the standard library introduces custom errors. Take a look at SyntaxError
from the encoding/json
package:
type SyntaxError struct {
Offset int64 // error occurred after reading Offset bytes
// contains filtered or unexported fields
}
func (e *SyntaxError) Error() string { return e.msg }
(source)
Also, a struct implementing the error
interface has no performance implications and does not consume more memory over a string implementation. See Go Data Structures.
Your testerr package works pretty well but it looses a major feature of the "struct-based" standard error package: That of un-equality:
package main
import ( "fmt"; "testerr"; "errors" )
func main() {
a := testerr.New("foo")
b := testerr.New("foo")
fmt.Println(a == b) // true
c := errors.New("foo")
d := errors.New("foo")
fmt.Println(c == d) // false
}
With errorString
being a plain string different errors with the same string content become equal. The original code uses a pointer to struct and each New
allocates a new struct so the different values returned from New
are different if compared with ==
albeit the equal error text.
No compiler is allowed to produce the same pointer here. And this feature of "different calls to New produce different error values" is important to prevent unintended equality of errors. Your testerr can be modified to yield this property by having *errorString
implement Error
. Try it: You need a temporary to take the address of. It "feels" wrong. One could imagine a fancy compiler which internalises the string values and might return the same pointer (as it points to the same internalised string) which would break this nice inequality property.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With