Go Function Types as Interface Values - go

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.

Related

What does it mean for a function to have a method?

I'm seeing the following type definition:
type Getter func(ctx *context.T, key string) (Ticket, error)
And the following method defined on it:
func (g Getter) GetData(ctx *context.T, path ...string) (data []byte, err error) {
…
}
I'm also seeing the following variable definition:
var Client Getter = func(ctx *context.T, key string) (Ticket, error) {
return …
}
where:
type Ticket interface {
…
}
What'll happen with this code exactly?
tl;dr It won't compile because Cannot use 'func() (MyData, error) { return nil, nil }' (type func() (MyData, error)) as the type Getter. In other words, MyGetter does not match the type of Getter. Here's a go playground link.
There are a couple of things going on here:
You're creating a type called Getter. Any value that fulfills this Getter type must be a function with zero input parameters and zero output parameters (keep this in mind for a second).
Go has first-class functions which means they can be assigned to variables, passed to parameters of other functions, or in this case methods can be added to a type whose underlying type is a function.
Lastly, you're trying to assign a function with the signature func() (MyData, error) to the type Getter which is where you're getting the compile error I mentioned in the tl;dr. This compile error is because your type Getter specifies that any value of this type must be a function with zero input parameters and zero return parameters. You're trying to assign a function that does have return parameters.

String function in syscall.Errno

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

Confused: implement multiple interfaces in Go [duplicate]

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.

Why is errorString a struct, not a string

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.

convert function type in Golang

// Each type have Error() string method.
// The error built-in interface type is the conventional interface for
// representing an error condition, with the nil value representing no error.
// type error interface {
// Error() string
// }
func (f binFunc) Error() string {
return "binFunc error"
}
func func_type_convert() {
var err error
err = binFunc(add)
fmt.Println(err)
fmt.Println(i)
}
I have two questions about the code above:
I don't know why the Error method executed, when add function was converted into binFunc type?
Why the add function converted result was able to assign to an err error interface variable?
error is an interface:
type error interface {
Error() string
}
This means that any type which has a method: Error() string fulfills the interface and can be assigned to a variable of type error.
binFunc has such a method:
func (f binFunc) Error() string {
return "binFunc error"
}
New developers in Go sometimes find this confusing because they don't realize it's possible to attach methods to more than just structs. In this case binFunc is defined liked this:
type binFunc func(int, int) int
So the way this works is you are allowed to convert any function which has the same signature: (from the spec)
A function type denotes the set of all functions with the same parameter and result types.
So if you create a function add:
func add(x, y int) int {
return x + y
}
You are allowed to convert this into a binFunc:
binFunc(add)
And because of the Error method on binFunc we defined above, we are then able to assign this new binFunc to a variable of type error:
var err error
var bf binFunc = binFunc(add)
err = bf
fmt.Println's behavior is to call .Error() on errors for you:
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).
So to answer your questions:
The Error method is executed because fmt.Println looks for arguments of type error, invokes .Error() and prints the resulting string.
You are allowed to assign binFuncs to err because binFunc has an Error method. You cannot assign add directly to err because it does not have an Error method. But you are allowed to convert add to a binFunc because they have the same function signature, and by doing so you can then assign it to the err variable.
go spec dependencies:
type casting or conversion -> assignability -> type identity
explicit type casting or conversion
binFunc and func(int, int) int have same underlying representation.
binFunc(add)
note, type casting can happen between 2 types that have the same underlying representation. However, their type can be totally different.
type MyInt int
func main() {
var b MyInt = 3
a := int(b)
fmt.Println(a, b)
}
variable assignment
check type identity
based on type identity rule, binFunc is identical to func(int, int) int. So you can do type casting as below:
A named type is always different from any other type. Otherwise, two types are identical if their underlying type literals are structurally equivalent; that is, they have the same literal structure and corresponding components have identical types.
func(int, int) int
is type literal, and it's unnamed.
type binFunc func(int, int) int
is a named type.
Predeclared types, defined types, and type parameters are called named types. An alias denotes a named type if the type given in the alias declaration is a named type.
named type is different from others. but here, binFunc is compared with the un-named type: their underlying type literals are structurally equivalent, both func(int, int) int.
var bfunc binFunc = add
check assignability
variable of named type can be assigned with a value of unnamed type providing their underlying type is identical.
You may call this an implicit type conversion here, but it's not accurate. At least golang doesn't call this type conversion because the underlying type/representation is the same.
inspired by this answer
extra words
type assertion only requires type identity. Therefore, the rule is simpler than type assignability.

Resources