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

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.

Related

How to pass type to function argument in Go

ERROR: type CustomStruct is not an expression.
type CustomStruct struct {
}
func getTypeName(t interface{}) string {
rt := reflect.TypeOf(t).Elem()
return rt.Name()
}
getTypeName(CustomStruct)
How can I pass struct type to function without type instance?
This will work
getTypeName((*CustomStruct)(nil))
But I wonder if there is more simple version..
You can't. You can only pass a value, and CustomStruct is not a value but a type. Using a type identifier is a compile-time error.
Usually when a "type" is to be passed, you pass a reflect.Type value which describes the type. This is what you "create" inside your getTypeName(), but then the getTypeName() will have little left to do:
func getTypeName(t reflect.Type) string {
return t.Name()
}
// Calling it:
getTypeName(reflect.TypeOf(CustomStruct{}))
(Also don't forget that this returns an empty string for anonymous types such as []int.)
Another way is to pass a "typed" nil pointer value as you did, but again, you can just as well use a typed nil value to create the reflect.Type too, without creating a value of the type in question, like this:
t := reflect.TypeOf((*CustomStruct)(nil)).Elem()
fmt.Println(t.Name()) // Prints CustomStruct
Lets resurrect this!
The generics proposal for Go got approved, and that's coming, eventually. When this question was first asked, this probably made more sense as a question, but for anyone looking to implement a generics pattern now, I think I've got an alright API for it.
For now, you can't interact with abstract types, but you can interact with methods on the abstract type, and reflect allows you to examine function signatures. For a method, the 0th is the receiver.
type Example struct {int}
type Generic struct{reflect.Type}
func (p Example) Type() {}
func Reflect(generic interface{}) Generic {
real := reflect.TypeOf(generic)
if real.Kind() != reflect.Func || real.NumIn() < 1 {
panic("reflect.Type.In(n) panics if not a func and if n out of bounds")
}
return Generic{real.In(0)}
}
func (g Generic) Make() interface{} {
return reflect.Zero(g.Type).Interface()
}
func main() {
tOfp := Reflect(Example.Type)
fmt.Printf("Name of the type: %v\n", tOfp.Name())
fmt.Printf("Real (initial)value: %v\n", tOfp.Make())
}
Some quick notes:
The structure of "Example" doesn't matter, rather only that it has a method with a non-pointer receiver.
The definition of a type called "Generic" as a struct is to accomplish what I believed OP's actual intent to be.
The above definition of "Generic" is a struct instead of an interface so that it can have its own method set. Defining "Generic" as an interface, and using a methodset specific to each operand-type used with it would make tons of sense.
If you weren't aware, actual generics are coming in Go 1.18. My example above has no linter or compile protection, and will panic at runtime if used incorrectly. It does work, and will let you reason over abstract types while you wait for a native implementation.
Happy Coding!
From Go version 1.18 a new feature Generics has been introduced. In most of the case instead of passing types to function, we can use generics. Then we will also get compile time error instead of runtime error and it's more efficient than reflect also.
Example Code
func HttpGet[T](url, body) T {
var resp T
return T
}
resp := HttpGet[ResponseType]("dummy.example", nil)

How to return a struct variable in a method

This program does not compile. It shows that the Env method is not available on the struct sample on method call. I can access the Env variable directly in the main program, but I'm curious to know why this is not compiling.
package main
//Sample is a struct
type Sample struct {
Env string
}
func main() {
pa := &Sample{Env: "acd"}
pa.call()
}
func (p *Sample) call() *Sample.Env {
return &p.Env
}
Function types are defined by the keyword func, followed by an optional receiver, followed by the function name, the parameter list between parentheses (which may be empty), and the result list (which may be empty), and then the function body.
The result list (zero or more types) is the contract for what the function will return. It can be a list of types or a list of named parameters including their types.
In your function definition for call():
func (p *Sample) call() *Sample.Env {
Your result list does not meet this expectation. *Sample.Env is a pointer to the Env property of type Sample, but not a type itself.
Your function returns value &p.Env. If you modify your function signature so that the result list is simply the type of &p.Env, then your program will run. p.Env is a string, therefore &p.Env is a pointer to a string. *string is the type "pointer to a string." Therefore if you change your function signature to this your code will work:
func (p *Sample) call() *string {
See:
Function Types
Types
Post script
The return type of call() is *string -- that is, a pointer to a string. So to print it you simply dereference it to a string with the asterisk:
env := pa.call()
fmt.Printf("Env is %s\n", *env)

Go Function Types as Interface Values

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.

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.

Go error: Final function parameter must have type

I have an issue with my function. I'm getting a
final function parameter must have type
For this method
func (s *BallotaApi) PostUser(c endpoints.Context,userReq Users) (userRes Users, error) {
c.Debugf("in the PostUser method")
user := userManger.login(userReq)//return a Users Type
return user, nil
I read those threads but I can't figure out where I'm wrong. It looks like I declared everything.
can-you-declare-multiple-variables-at-once-in-go
go-function-declaration-syntax
If you are naming your return parameters, you must name all of them
(userRes Users, err error)
That way, return statements can apply.
As mentioned in Function type:
Within a list of parameters or results, the names (IdentifierList) must either all be present or all be absent.
If you try to name one and not the other, as in this example, you will get:
func a() (b int, error) {
return 0, nil
}
# command-line-arguments
/tmp/sandbox170113103/main.go:9: final function parameter must have type
Dave C reminds us that:
Named returns should normally be limited to helping make better/clearer godoc documentation or when you need to change return values in a deferred closure.
Other than that they should be avoided.

Resources