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.
Related
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.
How to get the tags from a struct field in Go? I have a nested struct which I want to pass as an argument to another function and read the tags there. I know that by accessing it as a field is possible, but I am searching for a way to it.
type MyStruct struct {
Nested struct{} `bson:"nested"`
}
func main() {
val := reflect.ValueOf(MyStruct{})
val.Type().Field(0).Tag.Get("bson") // I want to avoid this
val := reflect.ValueOf(MyStruct{}.Nested)
val.Tag???
}
The tag you want to access belongs to MyStruct. If you pass the value of the Nested field, a copy is made which will be completely detached from MyStruct. There's no way to tell if the value passed originates from a field of MyStruct or from another struct, or from any other source (e.g. from a composite literal). So this is not possible.
var cache = struct {
sync.Mutex
mapping map[string]string
} {
mapping: make(map[string]string),
}
This looks like a struct with an embedded field sync.Mutex but I can't get my head around the second set of braces. It compiles and executes but what's up? Why does the label on the make instruction matter (it does) and the comma? Thanks...
The example you have is equivalent to:
type Cache struct {
sync.Mutex
mapping map[string]string
}
cache := Cache{
mapping: make(map[string]string),
}
Except in your example you do not declare a type of Cache and instead have an anonymous struct. In your example, as oppose to my Cache type, the type is the entire
struct {
sync.Mutex
mapping map[string]string
}
So think of the second pair of braces as the
cache := Cache{
mapping: make(map[string]string),
}
part.
make is a built in function that works similarly to C's calloc() which both initialize a data structure filled with 0'd values, in Go's case, certain data structures need to be initialized this way, other's (for the most part structs) are initialized with 0'd values automatically. The field there is needed so that the compiler now's cache.mapping is a empty map[string]string.
The comma there is part of Go's formatting, you can do Cache{mapping: make(map[string]string)} all on one line, but the moment the field's assignment is on a different line than the opening and closing braces, it requires a comma.
This is called a "struct literal" or an "anonymous struct" and is, in fact, how you always create structs in Go, it just may not be immediately obvious since you might be used to creating new types for struct types to make declaring them a bit less verbose.
An entire struct definition is actually a type in Go, just like int or []byte or string. Just as you can do:
type NewType int
var a NewType = 5 // a is a NewType (which is based on an int)
or:
a := 5 // a is an int
and both are distinct types that look like ints, you can also do the same thing with structs:
// a is type NewType (which is a struct{}).
type NewType struct{
A string
}
a := NewType{
A: "test string",
}
// a is type struct{A string}
a := struct{
A string
}{
A: "test string",
}
the type name (NewType) has just been replaced with the type of the struct itself, struct{A string}. Note that they are not the same type (an alias) for the purpose of comparison or assignment, but they do share the same semantics.
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.
So I can have
struct {
int
x []int
}
However,
struct {
int
[]int
}
will result in a syntax error: unexpected [, expecting }. Is there a way of having unnamed arrays in structs in Go? If so, what's the correct syntax?
Read The Go Programming Language Specification. In particular, the section on Struct types. The Go term to describe what you are looking for is an anonymous field.
Such a[n] [anonymous] field type must
be specified as a type name T or as a
pointer to a type name *T, and T
itself may not be a pointer type.
int is a type name. []int is neither a type name nor a pointer to a type name.
No, the type of an anonymous field must be a type name or a pointer to a type name. You could declare a new type name that is the same as an array type, and then it would work, but it wouldn't be exactly the same.