golang struct from other package - go

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

Related

Get the type name of a generic struct without type parameters

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.

Implicit type with struct of slices

If I have a file like this:
package main
import "fmt"
type Language struct {
Example []string
Link []string
}
func main() {
o := Language{
{".go", "go.ps1"},
{"golang.org", "go.dev"},
}
fmt.Println(o)
}
I get this result:
missing type in composite literal
I found I can resolve like this:
[]string{".go", "go.ps1"},
[]string{"golang.org", "go.dev"},
but is that strictly required? I would think Go would know the type of each
property based on the struct definition.
The problem here is not that the compiler doesn't know the type, it is that the syntax for a composite literal requires the type:
https://golang.org/ref/spec#Composite_literals
A string array literal is []string{"a","b",","c"}, not {"a","b","c"}.
You need field in struct definition, like this
o := Language{
Example: []string{".go", "go.ps1"},
Link: []string{"golang.org", "go.dev"},
}
fmt.Println(o)

How to correctly import method from another package

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

go generate: stringer: invalid operation: has no field or method String

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.

Construct struct literal with embedded structs

How do I construct a struct literal with embedded struct?
Go:
package main
import "fmt"
type Ping struct {
Content struct {
name string
}
}
func main() {
p := Ping{Content{"hello"}}
fmt.Println(p)
}
http://play.golang.org/p/UH4YO6CAFv
This works if I had written the structs this way:
Go:
type Ping struct {
Content
}
type Content struct {
name string
}
http://play.golang.org/p/ERGsO4CMEN
How do I do it with the embedded struct version in the first code version?
You can't, and you really shouldn't either, but if you insist anyway you can use something like:
p := Ping{struct{ name string }{"don't do it"}}
or
p := Ping{}
p.Content.name = "hello"
playground
This doesn't seem to be supported, looking at the spec for Struct type
A field declared with a type but no explicit field name is an anonymous field, also called an embedded field or an embedding of the type in the struct.
An embedded type must be specified as a type name T or as a pointer to a non-interface type name *T, and T itself may not be a pointer type.
That means T must be defined somewhere else.

Resources