Reading through the 7.8 section of "The Go Programming Language" I spotted following code:
var err error = syscall.Errno(2)
fmt.Println(err.Error()) // "no such file or directory"
fmt.Println(err) // "no such file or directory"
I understand the first and second line. error interface is saitisfied by syscall.Errno, thus Error() function returning string is available.
I don't understand third one. Going through syscall's sources I can't find any place where syscall.Errno satisfies stringer interface - String() function is not defined.
Why third one prints string representation of sysscall.Errno?
The answer is found in the fmt documentation here:
If the format (which is implicitly %v for Println etc.) is valid for a string (%s %q %v %x %X), the following two rules apply:
If an operand implements the error interface, the Error method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be formatted as required by the verb (if any).
So actually, for any value that supports both, the String() method is never called at all, since the error interface takes precidence over the Stringer interface. You can test this with a program like this one:
package main
import (
"fmt"
)
type foo string
func (f foo) String() string {
return "string"
}
func (f foo) Error() string {
return "error"
}
func main() {
fmt.Println(foo(""))
}
Output:
error
Related
The program below prints "2.5"
package main
import (
"fmt"
)
type myFloat64 float64
// func (f myFloat64) Error() string {
// return fmt.Sprintf("Error with %v", f)
// }
func main() {
var a myFloat64 = myFloat64(2.5)
fmt.Printf("%v\n", a)
}
Interestingly, when I uncomment the Error() method, the program no longer prints "2.5"
Question:
Why does adding an Error() method change program behavior? Pointers to the Go language specification explaining this behavior would be much appreciated.
myFloat64 implements the error interface:
type error interface {
Error() string
}
fmt.Println() will consider a as an error value, and print the error message by calling a.Error(), which executes fmt.Sprintf("Error with %v", f). But Sprintf behaves just like Println, it also considers f as an error value and calls Error(). This recursion goes infinitely and causes stack overflow.
I have been reading about function types as interface values in go and I came across an example that I have not been able to figure out. Here it is:
type binFunc func(int, int) int
func add(x, y int) int { return x + y }
func (f binFunc) Error() string { return "binFunc error" }
func main() {
var err error
err = binFunc(add)
fmt.Println(err)
}
You can find it on Go Playground here.
I understand that you can assign a method to a function type, but I do not understand how Error() is being called.
The docs for the fmt package have this to say:
Except when printed using the verbs %T and %p, special formatting
considerations apply for operands that implement certain interfaces.
In order of application:
...
If the format (which is implicitly %v for Println etc.) is valid for a
string (%s %q %v %x %X), the following two rules apply:
If an operand implements the error interface, the Error method will be invoked to convert the object to a string, which will then be
formatted as required by the verb (if any).
If an operand implements method String() string, that method will be invoked to convert the object to a string, which will then be
formatted as required by the verb (if any).
In other words, fmt.Println will attempt to print the string representation of the interface. Since the error interface is satisfied by binFunc, it invokes the Error method of binFunc.
This question already has an answer here:
fmt.Println calling Error instead of String()
(1 answer)
Closed 2 months ago.
In the below code the type ErrNegativeSqrt implements both Stringer and error interfaces. Since in Sqrt method the return type is fmt.Stringer, I would except the execution result to be:
0 nil
0 Impl Stringer type
But the actual result is the following, why?
0 nil
0 Impl error type
package main
import (
"fmt"
)
type ErrNegativeSqrt float64
func Sqrt(x ErrNegativeSqrt) (float64, fmt.Stringer) {
if x < 0 {
return 0, ErrNegativeSqrt(x)
}
return 0, nil
}
func (e ErrNegativeSqrt) String() string {
return "Impl Stringer type"
}
func (e ErrNegativeSqrt) Error() string {
return "Impl error type"
}
func main() {
fmt.Println(Sqrt(2))
fmt.Println(Sqrt(-2))
}
The documentation for Package fmt states that:
... special formatting
considerations apply for operands that implement certain interfaces.
In order of application:
...
If the %v verb is used with the # flag (%#v) and the operand implements the GoStringer interface, that will be invoked.
If the format (which is implicitly %v for Println etc.) is valid for a
string (%s %q %v %x %X), the following two rules apply:
If an operand implements the error interface, the Error method will be invoked ...
If an operand implements method String() string, that method will be invoked ...
Since you are using fmt.Println, rules 4 and 5 come into play, which prefers to call Error() over String()
You're ignoring the fact that your function returns a fmt.Stringer by passing the resulting value to fmt.Println as an interface{}.
You see the result from the Error() method only because the fmt package checks for the error interface before it checks for the fmt.Stringer interface.
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.
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 Neware 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.
I list the code from the book "Programming in Go".
I test it but it didn't work well.
error: "not enough arguments in call to BitFlag.String"
Goplayground Code: http://play.golang.org/p/FG23LdS_xK
type BitFlag int
func main() {
flag := Active | Send
BitFlag.String();
}
func (flag BitFlag) String() string {
...
}
Why do I see this error message?
You need to call String on an instance of BitFlag (here 'flag'), not on the BitFlag type itself.
flag := Active | Send
fmt.Println(strconv.Itoa(int(flag)))
fmt.Println(flag.String())
See this play.golang.org example.
Output:
3
3(Active|Send)