Construct struct literal with embedded structs - go

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.

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)

Why can't we embed slices or maps into Go structs

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.

Are interfaces only ever implemented by struct data types in Go?

I'm quite new to Go and was looking at Interfaces and their implementations.
All examples I have encountered use struct{} to implement any interface.
Is it possible to use any basic type?
The Go Programming Language Specification
Method declarations
A method is a function with a receiver. A method declaration binds an
identifier, the method name, to a method, and associates the method
with the receiver's base type.
The receiver is specified via an extra parameter section preceding the
method name. That parameter section must declare a single non-variadic
parameter, the receiver. Its type must be of the form T or *T
(possibly using parentheses) where T is a type name. The type denoted
by T is called the receiver base type; it must not be a pointer or
interface type and it must be defined in the same package as the
method. The method is said to be bound to the base type and the method
name is visible only within selectors for type T or *T.
No. It can be any type other than a pointer or interface type.
For example, using string as the underlying type,
package main
import (
"fmt"
"strings"
)
type Caser interface {
Upper() string
Lower() string
}
type Str string
func (s Str) Upper() string {
return strings.ToUpper(string(s))
}
func (s Str) Lower() string {
return strings.ToLower(string(s))
}
func main() {
str := Str("Forty-Two")
fmt.Println(str)
up := str.Upper()
fmt.Println(up)
lo := str.Lower()
fmt.Println(lo)
}
Playground: https://play.golang.org/p/9RDRTftqWot
Output:
Forty-Two
FORTY-TWO
forty-two

What are the second pair of braces in this Golang struct?

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.

Resources