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
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.
Given the following three go packages (I abbreviated the import paths for imp and Tdef for privacy reasons)
package main
import (
"imp"
"fmt"
)
func main() {
T := imp.NewT()
fmt.Printf("T.X = %d\n", T.X)
fmt.Printf("T has type %T\n", T)
}
package imp
import (
"Tdef"
)
func NewT() Tdef.T {
return Tdef.T{0,0}
}
package Tdef
type T struct {
X int
Y int
}
func (T T) GetX() int {
return T.X
}
main() produces the output
T.X = 0
T has type Tdef.T
This means that I can use a variable of type T in a package where the definition of T is not visible by defining it using a return value, and I can use its exported fields (and methods, not shown here).
I find that a bit surprising and did not find any information on this in the spec. Then again, I don't have much programming experience -- is this to be expected, and is it bad practice to make use of it (I don't have a real-life example right now, mind you)?
You are allowed to use values originating from anywhere, but without the import declaration you can't refer to its type for example. Yes, because referring to identifiers requires a qualified identifier which has the form of:
QualifiedIdent = PackageName "." identifier .
You haven't imported Tdef in your main package, so you can't refer to the type Tdef.T identifier (type), but you didn't attempt it so all is good.
Spec: Import declarations:
An import declaration states that the source file containing the declaration depends on functionality of the imported package (§Program initialization and execution) and enables access to exported identifiers of that package. The import names an identifier (PackageName) to be used for access and an ImportPath that specifies the package to be imported.
You used a short variable declaration and so you did not specify the variable's type (which is perfectly OK), so you didn't need to refer to its type:
T := imp.NewT()
T's type will be inferred from the right-hand side expression, which will be Tdef.T of course, but your source code contains no reference to this type. Obviously the following line would give a compile-time error without importing Tdef:
var T Tdef.T = imp.NewT()
You can even return values of unexported types from an exported function, and the package importing it can as well use it, e.g. it can print it, call exported methods and access exported fields of the value of the unexported type (although returning values of unexported types is considered bad practice, golint gives a warning like: "exported func XXX returns unexported type pkgname.typename, which can be annoying to use").
Situation:
I've the following project structure:
root
parser
parser.go
builtin
exit.go
hi.go
structs
base_structs.go
main.go
.. and the base_structs.go file looks like this:
package structs
type Built_in_func func([] string)
type Built_in struct {
s string
f Built_in_func
}
I've imported the package in my main.go and I'm referencing the struct with structs.Built_in.
This is what I'm trying to do:
var builtin_list [] structs.Built_in
builtin_list = append(builtin_list, structs.Built_in{s:"exit", f:builtin.Exit})
builtin_list = append(builtin_list, structs.Built_in{s:"hi", f:builtin.Hi})
But I'm getting this error:
unknown structs.Built_in field 's' in struct literal
Question:
What am I doing wrong?
In Go, the visibility of a name outside a package is determined by whether its first character is upper case.
So the field s is actually not visible from outside the package structs and you get that error.
If you define your struct like (note the upper case):
type Built_in struct {
S string
F Built_in_func
}
Then this will work (again the upper case):
structs.Built_in{S:"exit", F:builtin.Exit}
You can read more here:
https://golang.org/doc/effective_go.html#names
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.
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.