【Golang】struct

Posted by 西维蜀黍 on 2020-03-15, Last Modified on 2021-11-13

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
}

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