I'm trying to use the stringer cmd so that I can generate String() methods for some int types. Here is how the code looks like
//go:generate stringer -type=MyIntType
type MyIntType int
const (
resource MyIntType = iota
)
func myfunc(){
print(resource.String())
}
The error I'm getting on go generate command is invalid operation: resource (constant 0 of type MyIntType) has no field or method String which makes sense because there is no String method yet. How am I supposed to fix this error if stringer cmd is supposed to actually generate the String method? Should I use fmt.Sprintf("%s", resource) all over the code ? it looks a bit ugly to me. At least not as nice as resource.String().
Each file must be parsed and type checked by the types library before the methods are generated. This usually doesn't pose a problem, since the String() method is rarely called directly, and is used by passing a value to something like fmt.Println that always checks first for a Stringer.
You can either not call String() yourself:
file: type.go
//go:generate stringer -type=MyIntType
package painkiller
import "fmt"
type MyIntType int
const (
resource MyIntType = iota
)
func myfunc() {
fmt.Println(resource)
}
Or you can put the calls in another file, and pass only the files that stringer needs as arguments. With no arguments, stringer checks the package as a whole, but if you provide only a list of files, they assume that some methods may be defined in files not provided.
file: type.go
//go:generate stringer -type=MyIntType type.go
package foo
type MyIntType int
const (
resource MyIntType = iota
)
file myfunc.go
package foo
func myfunc() {
print(resource.String())
}
The stringer cmd calls the go/parser.ParseFile for every go file. Thus if you have a method that is not declared it will fail. You will have to use fmt.Sprint* statements to get over this. Or you could tell go generate to only generate a specific file.
I don't know if we could term this as a bug. You could file it, probably see what the response is.
Related
Say I have a generic struct called foo and I create two objects from it. I can determine the concrete type of each using reflect.TypeOf(), like so:
package main
import (
"fmt"
"reflect"
)
type foo[T any] struct {
data T
}
func main() {
a := foo[string]{"cheese"}
b := foo[int]{42}
fmt.Println(reflect.TypeOf(a))
fmt.Println(reflect.TypeOf(b))
}
// main.foo[string]
// main.foo[int]
What I am interested in is determining just the generic type of these objects (i.e., foo) and not the concrete type (i.e., foo[string] and foo[int]). Is this possible or do I need to manually extract the generic type from these strings (e.g., with regex)?
Edit
Regex might look something like this:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
fmt.Println(GetGenericType(a))
fmt.Println(GetGenericType(b))
// foo
// foo
I've also seen this question but this doesn't answer this question because it gives the concrete type (i.e., main.foo[string]) rather than the generic type (i.e., foo).
Reflection doesn't see the name of the "base" generic type, because at run time that base type doesn't exist.
The relevant passage from the Go spec is Instantiations:
Instantiating a type results in a new non-generic named type; instantiating a function produces a new non-generic function.
So when you write:
b := foo[int]{42}
name := reflect.TypeOf(b).Name()
the name of that type is precisely foo[int].
It's worth noting that the identifier foo without the type parameter list is relevant at compile time, because it prevents you from redeclaring it in the same package. Type definitions:
A type definition creates a new, distinct type with the same
underlying type and operations as the given type and binds an
identifier, the type name, to it.
TypeDef = identifier [ TypeParameters ] Type .
But instantiations, as defined above, result in a new named type which is different than foo; and at run time when you can use reflection, you deal with instantiations only.
In conclusion, I think your solution with regex is acceptable, until some helper function is added to the stdlib (if ever). Reposting it here for clarity:
func GetGenericType(x any) string {
// Get type as a string
s := reflect.TypeOf(x).String()
// Regex to run
r := regexp.MustCompile(`\.(.*)\[`)
// Return capture
return r.FindStringSubmatch(s)[1]
}
Just keep in mind the difference between Type.String() and Type.Name(): any type can have a string representation, but only named types have a name. (Obviously, right?). So for example if you wrote:
b := &foo[int]{42}
then the type of b is *foo[int], which is an anonymous composite type, and Name() returns an empty string.
Just be curious, how to invoke the below go func func (t) test() if i declare it with type only receiver, is it even valid? like: https://play.golang.org/p/k8QukaCZdUN
package main
import (
"fmt"
)
type t int
func (t) test() {
fmt.Println(233)
}
func main() {
fmt.Println(666666)
}
I tried to build and run it, seems ok... but:
What exactly is this func (t) test() and how to invoke it?
Is there any meaningful use case for this kind of declaration (i mean bare type as receiver)?
More context:
I was triggered to think about this bcoz i want to find an idiomatic/elegant way to expose all values of an enum(iota) to external packages, currently i am doing something like: https://play.golang.org/p/iD0Aq_Mn2sj
package main
import (
"fmt"
)
type Gender int
const (
Male Gender = iota + 1
Female
)
func (g Gender) All() [2]string {
return [...]string{"MALE", "FEMALE"}
}
func main() {
fmt.Println("All gender types:", Gender(0).All())
}
It works, so any Gender instance can invoke its All function to get all the Gender types, but it feels a bit weird, so i start thinking of whether it is possible to register a func on the type only, not the instances, so i can do: package.Gender.All(), is it possible in go?
btw, no global var.
is it even valid?
As shown by your example, yes.
What exactly is this func (t) test() and how to invoke it?
It's a normal method. You invoke it like a normal method. See:
var val t
val.test()
Is there any meaningful use case for this kind of declaration (i mean bare type as receiver)?
The main difference with this form of method is that you can't access the receiver (as it's unnamed). So it's effectively similar to a "static" method (although it's really not. you still need an instance to call it)
If you want to get all values of an enum, there's no reason to make it a type method. All it does is create an extra step to call it:
var g Gender // create a useless variable so we can call the method
g.All() // actually call the method
You might as well make a regular function for it.
I have the following simple project:
myapp/
|- mainlib/main.go
|- tables/sometable.go
In sometable.go I have:
package tables
import (
"fmt"
)
type TableName string
func (table TableName) GetDataFromDataSource() string {
return "getdatafromdatasource"
}
Those are methods, and now I want to use them in main.go:
package main
import t myapp/tables
type tableName t.TableName // and such "attempts"
I've tried:
t.GetDataFromDataSource()
t.tableName("string").GetDataFromDataSource()
tableName("string").GetDataFromDataSource()
and similar combinations and compiler says "undefined reference" still...
So, my question is: how to correctly use method from another package in such context? Of course my methods starts with captial letters and are exported. If I try to use simple function with same name (not method), then it works correctly...
This has nothing to do with packages, but with alias declaration vs type definition.
Let's take a simple example:
type A string
func (a A) Print() {
fmt.Println(a)
}
type B A
type C = A
func main() {
A("A").Print()
B("B").Print()
C("C").Print()
}
This fails to compile on line B("B").Print() with:
B("B").Print undefined (type B has no field or method Print)
The spec clearly spells out the difference in the type declarations section
The line type B A is a type definition:
A type definition creates a new, distinct type with the same
underlying type and operations as the given type, and binds an
identifier to it.
While the line type C = A is an alias declaration:
An alias declaration binds an identifier to the given type.
The former is a new type, so the receiver type A for method Print() will not match the type B.
The latter is just a name pointing to type A.
You'll want to change your code to the following, using type aliases:
import t myapp/tables
type tableName = t.TableName
tableName("string").GetDataFromDataSource()
And a few notes:
t.GetDataFromDataSource() can't work, it's a method and needs a receiver
t.tableName("string").GetDataFromDataSource() won't work, t.tableName is not a thing, or if it is, it's not exported
you can't talk about importing methods, only packages are imported. A method is either exported or not, but it's the package you import
Consider this code
import (
"fmt"
"math/big"
)
func main() {
var b1,b2,b3,bigSum big.Float
b1.SetFloat64(25.3)
b2.SetFloat64(76.2)
b1.SetFloat64(53.1)
bigSum.Add(&b1, &b2).Add(&b3, &bigSum)
fmt.Println(bigSum) // {53 0 0 1 false [9317046909104082944] 8}
fmt.Println(&bigSum) // 129.3
}
I have 2 questions
Why I have to pass bigSum as reference (by using &) to get the correct answer, otherwise we'll get back an object?
How does Println work in Go? I mean how does it know which format it should apply for different types?
Println determines whether the value implements the Stringer interface. If it does then it will call the String() to get formatted value. big.Float implements it for pointer receiver so you have to pass a reference. Otherwise Println will detect that it's a struct and print all of it's fields using reflection
Go is open sourced. You can see for yourself https://golang.org/src/fmt/print.go?#L738 It uses type switches and reflection.
Are there partial classes in Go?
Like the one here in C#?
http://msdn.microsoft.com/en-us/library/wa80x488.aspx
I suppose there isn't any as you cannot partially declare structs.
The method declarations for a type do not need to be in the same source file as the type declaration or other method declarations for the type. The method declarations do need to be in the same package as the type declaration.
A type declaration cannot be split across files.
Go does not have classes.
In Go you can have a method associated with any type within the same package in any file. Take this small example of object foo with function Bar.
package main
import "fmt"
type foo struct {} // put variables associated with the type here
func ( /*f*/ foo) Bar() { // put a value in front of foo if u need to access any elements in the object
// do something interesting
fmt.Println("Method called :D")
}
func main() {
example := foo{}
example.Bar()
}
As long as foo and bar declarations occur in the same package they can be placed in any file.
I hope this demonstrated the desired functionality you are trying to implement / wondering if Go can support.