I'd to learn whether it's possible to initialize an array of anonymous struct contains generic-field. Code below doesn't compile, moreover I couldn't find any sample related to:
testCases := type [K comparable, T Numeric] []struct {
name string
args map[K]T
expected int
}{
// items ...
{"integer", map[string]int{ "a":1 },
}
Without anonymous structs it's easy, but not the aimed:
type args[K comparable, T Numeric] struct {
m map[K]T
}
testCases := []struct {
name string
args args[string, int]
expected int
}{}
Thanks!
Type parameters are introduced so when you instantiate the type, you can give concrete types to type parameters. Given that, there is no sense in what you want to do. You want to create a generic anonymous type and instantiate it right away. You can't use this anonymous struct elsewhere (because it's anonymous), so leave out the type parameters and use the concrete types you'd instantiate the type parameters with if it would be a named type.
And to answer your original question: no, you cannot do this. The syntax does not allow this. There was a proposal to support this but was rejected: proposal: spec: generics: Anonymous generic aggregate types #45591. The workaround is to use a named struct type instead of the anonymous struct type, just as you suggested.
Related
This question already has an answer here:
Go generics: invalid composite literal type T
(1 answer)
Closed last month.
The following code results in the error "invalid composite literal type T".
package main
import "fmt"
func main() {
fmt.Println(createThing[foo]())
}
type thing interface {
foo | bar
}
type foo struct {
id int
a string
}
type bar struct {
id int
b int
}
func createThing[T thing, P *T]() P {
return &T{}
}
If I only include foo in interface thing, or remove a string and b int so foo and bar are exactly the same, the code will run without error. However, doesn't this defeat the purpose of generics? Why can't I instantiate a generic type like this, especially when I'm not even accessing any fields?
Possibly related to https://github.com/golang/go/issues/48522
Most generic types are not valid types for composite literals. This isn't a problem though, as there are other ways to create values of generic types.
To create a pointer to a new zero value:
func createThing[T thing]() *T {
return new(T)
}
Or to create a non-pointer zero value:
func createThing[T thing]() T {
var value T
return value
}
As for why the error occurs in this way, here's the explanation from the spec, revised to address your specific question.
For composite literals:
The LiteralType's core type T must be a struct, array, slice, or map type
What is the core type?
An interface T has a core type if [...] there is a single type U which is the underlying type of all types in the type set of T
No other interfaces have a core type.
What is the underlying type?
Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its declaration.
A "type literal" can refer to a literal struct type, like struct{int id}. So, when foo and bar both have an underlying type of struct{int id}, then thing has a core type of struct{int id}, and so composite literals are possible. When foo and bar don't have the same underlying type, then thing has no core type, and composite literals are not possible, hence your error.
The formal definition may seem complicated, but the result and practical takeaway is simple: generic code is only capable of expressing common behaviour across the possible types. Besides in the special case where all underlying types are the same, literal values are not a common behaviour.
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.
In the following code I have a token type that contains a literal value. By using an empty interface I can create a slice of tokens and append tokens that have different types. I don't think it's possible to accomplish the same using generics since we wouldn't be able to infer a type for the Slice of tokens. Is this assumption correct?
type Token struct {
TokenType string
Literal interface{}
}
func main(){
tok1 := &Token{TokenType: "string", Literal: "foo"}
tok2 := &Token{TokenType: "integer", Literal: 10}
tokS := []*Token{tok1, tok2}
}
No.
Given a parametrized Token type as:
type Token[T any] struct {
TokenType string
Literal T
}
each instantiation with a different type argument produces a different (named) type.
In other words, Token[string] is not assignable to Token[int]. Nor is it assignable to Token[any] as any here is used as a static type to instantiate T.
So when you construct a slice with a specific instance of Token[T any], different instances are simply not assignable to its element type:
tokS := []*Token[string]{tok1, tok2}
// invalid: cannot use tok2 (variable of type *Token[int]) as type *Token[string] in array or slice literal
The only slice that can hold different types, as Token[string] and Token[int] is []interface{} or []any.
since we wouldn't be able to infer a type for the Slice of tokens. Is this assumption correct?
Almost. More precisely, the slice of Token wouldn't infer anything because you yourself must construct it with a concrete instantiation of the generic type
Type inference is used to deduce missing type parameters from those already supplied for function arguments. Generic types must be explicitly instantiated, with a concrete type argument for each type parameter.
Is this assumption correct?
Yes. Your can have a slice of Token[string] or Token[int].
I learnt that it is not allowed to embed slices or maps into Go structs. But I found two workarounds to this:
Declare the slice or map outside the struct and embed it into the struct
var mySlice []int
type myStruct struct {
mySlice
}
I don't really understand this second workaround, but I found it's called Composition and it's done by just omitting the var keyword when declaring the slice or map within the struct
type myStruct struct {
mySlice []int
}
My first question is, can anyone explain why we can't directly embed slices and maps into a struct?
Second question: using the second workaround, is there any negative performance effect to that?
Third question: why do the first and second workaround work?
Spec: Struct types:
A field declared with a type but no explicit field name is called an embedded field. An embedded field 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. The unqualified type name acts as the field name.
You can't embed a type like map[int]string, that's an anonymous type. Spec requires a type name. Even if you could embed that, it contains brackets which would make it disqualify from being an identifier, and again, spec says the unqualified type name acts as the field name.
Simply create a new type for it which you can then embed:
type mymap map[int]string
type s struct {
mymap
}
When you embed a type in a struct, you omit the field name. In your second attempt:
type myStruct struct {
mySlice []int
}
You did not omit the field name (it's mySlice), so this is not embedding, it's a regular, named field.
This is a Go grammar question and seems a stupid question but I've been checking the Go language specification to find some official words or definitions to define what type is a xxx type, say, what type is an interface type?
For example, I see words like these:
The method set of an interface type is its interface.
Or
An embedded field 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.
Or
Consider a struct type T with two methods ...
type T struct {
a int
}
....
A type literal like struct {...} is a struct type, what about A in
type A struct {...} and B in type B interface{...}? Is A a struct type and B an interface type?
Yes, from the above sample for the struct type T, I can tell that a defined type (by the "type" declaration) whose given type is a struct type or interface type is also a struct or interface type. So A is a struct type and B is an interface type also. But where are the official definitions of this rule?
For defined types I can only find the following relating to type categories:
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.
So my understanding is that the defined type is a new, distinct type with the given type, but they are in the same type category, say, interface types or struct types. Still, there are no such definitions.
TLDR;
The kind of type T is interface if its underlying type is an interface type.
The kind of type T is struct if its underlying type is a struct type.
Spec: Struct types and Spec: Interface types specifies exactly what are the struct and interface types:
StructType = "struct" "{" { FieldDecl ";" } "}" .
FieldDecl = (IdentifierList Type | EmbeddedField) [ Tag ] .
EmbeddedField = [ "*" ] TypeName .
Tag = string_lit .
InterfaceType = "interface" "{" { MethodSpec ";" } "}" .
MethodSpec = MethodName Signature | InterfaceTypeName .
MethodName = identifier .
InterfaceTypeName = TypeName .
So for example these are struct types:
struct { A int }
struct {}
struct { _ int }
and these are interface types:
interface { String() string }
interface {}
We may use a type declaration to create a new type, such as:
type Point struct { X, Y int }
The above type definition creates a new, distinct type with the same underlying type and operations as the given type, and binds an identifier to it. The definition of underlying type is recursive:
Each type T has an underlying type: If T is one of the predeclared boolean, numeric, or string types, or a type literal, the corresponding underlying type is T itself. Otherwise, T's underlying type is the underlying type of the type to which T refers in its type declaration.
When we talk about arbitrary types being structs or interfaces, we're talking about their kind.
In the light of this, basically your question is equivalent to this:
"When is the kind of an arbitrary type interface or struct?"
The answer to this question is not in the spec, but this is how we could define it:
The kind of a type T is interface if its underlying type is an interface type.
Similarly:
The kind of a type T is struct if its underlying type is a struct type.
So for example:
type Point struct { X, Y int }
type PP Point
Is the type struct { X, Y int } of kind struct? Yes, because since it's a type literal, its underlying type is itself, and it's by definition a struct type.
Is Point a struct? Since the underlying type of Point is the underlying type of the type to which it refers in its type declaration, which is a type literal (see above), it is of struct type (its kind is struct).
Is PP a struct? Since its underlying type is the underlying type of the type to which it refers in its type declaration (which is Point), whose underlying type is a struct type literal, yes, it is also a struct type.
This kind we're talking about is represented by the reflect.Kind type. There are reflect.Interface and reflect.Struct constants (of type reflect.Kind) to represent the struct and interface kinds. And the reflect.Type type descriptor has a Type.Kind() method to access this kind.
This is how you can check if the type (kind) of some value is a struct for example:
func isStruct(i interface{}) bool {
return reflect.TypeOf(i).Kind() == reflect.Struct
}
Testing it (try it on the Go Playground):
fmt.Println(isStruct(Point{})) // true
fmt.Println(isStruct(PP{})) // true
fmt.Println(isStruct(struct{}{})) // true
fmt.Println(isStruct("text")) // false
Checking for interface type is a little more complicated because passing an interface value to a function that expects interface{} will not pass the interface value as-is but the concrete value "stored" in it, and as an interface{} value. We would have to pass a pointer to interface (which otherwise rarely makes sense in Go), access the element type and check its kind. For details, see this answer: What is the difference between reflect.ValueOf() and Value.Elem() in go?
An interface type is a type introduced by the interface keyword, or a name for such a type as defined by type name interface (plus of course the actual requirements for that interface).
Any type, whether it is an interface type or not, implements an interface type if it has the appropriate set of named methods. That is, a struct type may be sufficient to be used with some interface type. Even a non-struct type may be sufficient:
type foo int
func (receiver_arg foo) method1() { ... }
Type-name foo now implements any interface that requires a method named method1 (provided of course that it implements the rest of any required methods).
... the defined type [via the type keyword] is a new, distinct type with the given type, but they are in the same type category ...
Yes, just so. Using reflect, you'll find that they have the same Kind. The word kind isn't in the spec like this but it's quite useful, and the link here enumerates all the fundamental type-kinds in Go.