structs
A struct is a sequence of named elements, called fields, each of which has a name and a type. Field names may be specified explicitly (IdentifierList) or implicitly (EmbeddedField). Within a struct, non-blank field names must be unique.
package main
import "fmt"
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
v.X = 4
fmt.Println(v.X)
}
// An empty struct.
struct {}
// A struct with 6 fields.
struct {
x, y int
u float32
_ float32 // padding
A *[]int
F func()
}
Pointers to structs
Struct fields can be accessed through a struct pointer.
To access the field X
of a struct when we have the struct pointer p
we could write (*p).X
. However, that notation is cumbersome, so the language permits us instead to write just p.X
, without the explicit dereference.
package main
import (
"fmt"
"reflect"
)
type Vertex struct {
X int
Y int
}
func main() {
v := Vertex{1, 2}
m := v
p := &v
p.X = 1e9
fmt.Println(v)
fmt.Println(m)
fmt.Println(p)
fmt.Println(reflect.TypeOf(v))
fmt.Println(reflect.TypeOf(m))
fmt.Println(reflect.TypeOf(p))
}
//output
{1000000000 2}
{1 2}
&{1000000000 2}
main.Vertex
main.Vertex
*main.Vertex
Struct Literals
A struct literal denotes a newly allocated struct value by listing the values of its fields.
You can list just a subset of fields by using the Name:
syntax. (And the order of named fields is irrelevant.)
The special prefix &
returns a pointer to the struct value.
package main
import "fmt"
type Vertex struct {
X, Y int
}
var (
v1 = Vertex{1, 2} // has type Vertex
v2 = Vertex{X: 1} // Y:0 is implicit
v3 = Vertex{} // X:0 and Y:0
p = &Vertex{1, 2} // has type *Vertex
)
func main() {
fmt.Println(v1, p, v2, v3)
}
//output
{1 2} &{1 2} {1 0} {0 0}
Define Struct’s Fields
内嵌字段(Embedded Field)
A field declared with a type but no explicit field name is called an embedded field
.
// Golang program to illustrate the
// concept of inheritance
package main
import (
"fmt"
)
type Base struct {
b int
}
func (base *Base) Describe() string {
return fmt.Sprintf("base %d belongs to us", base.b)
}
type Container struct { // Container is the embedding struct
Base // Base is the embedded field
c string
}
func main() {
co := Container{}
co.b = 1
co.c = "string"
// Read the fields within embedded fields
fmt.Printf("co.b: %v - %v\n", co.b, &co.b)
fmt.Printf("co.Base.b: %v - %v \n", co.Base.b, &co.Base.b)
// Call the methods of embedded fields
fmt.Println(co.Describe())
fmt.Println(co.Base.Describe())
}
Output:
// Read the fields within embedded fields
co.b: 1 - 0xc00000c060
co.Base.b: 1 - 0xc00000c060
// Call the methods of embedded fields
base 1 belongs to us
base 1 belongs to us
Shadowing of embedded fields
What happens if the embedding struct has a field x
and embeds a struct which also has a field x
? In this case, when accessing x
through the embedding struct, we get the embedding struct’s field; the embedded struct’s x
is shadowed.
Here’s an example demonstrating this:
// Golang program to illustrate the
// concept of inheritance
package main
import (
"fmt"
)
type Base struct {
b int
tag string
}
func (base *Base) DescribeTag() string {
return fmt.Sprintf("Base tag is %s", base.tag)
}
type Container struct { // Container is the embedding struct
Base // Base is the embedded field
c string
tag string
}
func (co *Container) DescribeTag() string {
return fmt.Sprintf("Container tag is %s", co.tag)
}
func main() {
b := Base{b: 10, tag: "b's tag"}
co := Container{Base: b, c: "foo", tag: "co's tag"}
// Read the fields within embedded fields
fmt.Printf("co.tag: %v - %v\n", co.tag, &co.tag)
fmt.Printf("co.Base.tag: %v - %v \n", co.Base.tag, &co.Base.tag)
fmt.Println(b.DescribeTag())
fmt.Println(co.DescribeTag())
}
Output:
co.tag: co's tag - 0xc000122068
co.Base.tag: b's tag - 0xc000122048
Base tag is b's tag
Container tag is co's tag
Note that when accessing co.tag
, we get the tag
field of Container
, not the one coming in through the shadowing of Base
. We could access the other one explicitly, though, with co.Base.tag
.
Anonymous fields
You can define a struct type without declaring any field names. You have to just define the field data types and Go will use the data type declarations (keywords) as the field names.
package main
import "fmt"
type Data struct {
string
int
bool
}
func main() {
sample1 := Data{"Monday", 1200, true}
sample1.bool = false
fmt.Println(sample1.string, sample1.int, sample1.bool)
}
In the above program, we have defined only the data types on the Data
struct type. Go under the hood will use these field types as the name of the fields. There is no difference whatsoever between the struct type defined this way and the struct types we have defined earlier.
Just, in this case, Go helped us creating field names automatically. You are allowed to mix some anonymous fields with named fields like below.
type Employee struct {
firstName, lastName string
salary int
bool // anonymous field
}
Promoted fields
We can drop the field name of a nested struct and Go will use struct type as the field name. Let’s see an example.
package main
import "fmt"
type Salary struct {
basic int
insurance int
allowance int
}
type Employee struct {
firstName, lastName string
Salary
}
func main() {
ross := Employee{
firstName: "Ross",
lastName: "Geller",
Salary: Salary{1100, 50, 50},
}
fmt.Println("Ross's basic salary", ross.Salary.basic)
}
We can just used the Salary
struct type to create the anonymous field. Then we can use .
(dot notation) approach to get
and set
the values of the Salary
struct fields (nested struct of *ross*
) as usual.
Define Struct’s Methods
func (db *DB) PingContext(ctx context.Context) error {
...
}
db *DB
表示这个 PingContext
方法为 DB 这个 struct 的方法。
Receiver Type
Choosing whether to use a value or pointer receiver on methods can be difficult, especially to new Go programmers.
**If in doubt, use a pointer, but there are times when a value receiver makes sense, usually for reasons of efficiency, such as for small unchanging structs or values of basic type. **
Some useful guidelines:
- If the receiver is a map, func or chan, don’t use a pointer to them. If the receiver is a slice and the method doesn’t reslice or reallocate the slice, don’t use a pointer to it.
- If the method needs to mutate the receiver, the receiver must be a pointer.
- If the receiver is a struct that contains a sync.Mutex or similar synchronizing field, the receiver must be a pointer to avoid copying.
- If the receiver is a large struct or array, a pointer receiver is more efficient. How large is large? Assume it’s equivalent to passing all its elements as arguments to the method. If that feels too large, it’s also too large for the receiver.
- Can function or methods, either concurrently or when called from this method, be mutating the receiver? A value type creates a copy of the receiver when the method is invoked, so outside updates will not be applied to this receiver. If changes must be visible in the original receiver, the receiver must be a pointer.
- If the receiver is a struct, array or slice and any of its elements is a pointer to something that might be mutating, prefer a pointer receiver, as it will make the intention more clear to the reader.
- If the receiver is a small array or struct that is naturally a value type (for instance, something like the time.Time type), with no mutable fields and no pointers, or is just a simple basic type such as int or string, a value receiver makes sense. A value receiver can reduce the amount of garbage that can be generated; if a value is passed to a value method, an on-stack copy can be used instead of allocating on the heap. (The compiler tries to be smart about avoiding this allocation, but it can’t always succeed.) Don’t choose a value receiver type for this reason without profiling first.
- Don’t mix receiver types. Choose either pointers or struct types for all available methods.
- Finally, when in doubt, use a pointer receiver.
Ref https://github.com/golang/go/wiki/CodeReviewComments#receiver-type
Prove
package main
import (
"fmt"
)
func main() {
m := &testStruct{id: "original_value"}
fmt.Printf("%p\n", m) // 0xc000116220
m.ID()
fmt.Println(m.id) // original_value
m.ID2()
fmt.Println(m.id) // modify_by_ID2()
}
type testStruct struct {
id string
}
func (c testStruct) ID() string {
fmt.Printf("%p\n", &c) // 0xc000116230
c.id = "modify_by_ID()"
return c.id
}
func (c *testStruct) ID2() string {
fmt.Printf("%p\n", c) // 0xc000116220
c.id = "modify_by_ID2()"
return c.id
}
Miscellaneous
Anonymous Struct
In the below program, we are creating a struct indexRuneTest
without defining a derived struct type. This is useful when you don’t want to re-use a struct type.
var indexRuneTest = struct {
s string
out int
}{
s: "aa",
out: 11,
}
var config struct {
APIKey string
OAuthConfig oauth.Config
}
config.APIKey = "111"
var indexRuneTests = []struct {
s string
out int
}{
{"a A x", 2},
{"some_text=some_value", 9},
{"☺a", 3},
{"a☻☺b", 4},
}
Nested Struct
type Item struct {
Title string
URL string
}
type Response struct {
Data struct {
Children []struct {
Data Item
}
}
}
Nested interface
Since we know that, an interface type is a declaration of method signatures. Any data type that implements an interface can also be represented as a type of that interface (polymorphism).
Using this polymorphism principle, we can have a struct field of an interface type and its value can be anything that implements that interface. Let’s see this in action using the earlier example.
Reference
- https://golang.org/ref/spec
- https://talks.golang.org/2012/10things.slide#15
- https://medium.com/rungo/structures-in-go-76377cc106a2
- https://eli.thegreenplace.net/2020/embedding-in-go-part-1-structs-in-structs/
- https://golang.org/doc/effective_go
- https://github.com/golang/go/wiki/CodeReviewComments#receiver-type