Summary of variable differences of golang median value type and pointer type

  • 2020-06-12 09:19:08
  • OfStack

preface

Value types: All types such as int, float, bool, and string are value types that use variables that point directly to values that exist in memory. The value of a value type variable is stored on the stack. When you use the equal sign = to assign the value of one variable to another, such as j = i, you are actually copying the value of i in memory. Can be achieved by & i gets the memory address of the variable i

Pointer type: Simply put, the pointer type of go language and the pointer type usage of C/C++ are the same. In addition to security considerations, go language adds a number of restrictions, including the following:

Different types of Pointers cannot be converted to each other, such as *int, int32, and int64 Any normal pointer type *T and uintptr cannot be converted to each other Pointer variables cannot be operated on, such as C/C++, the ++ inside, -- operation

The differences between golang median/pointer type variables are described in more detail below, but let's start with 1.

Variable of value type and variable of pointer type

First declare 1 structure:


type T struct {
 Name string
}
func (t T) M1() {
 t.Name = "name1"
}
func (t *T) M2() {
 t.Name = "name2"
}

M1() Is the value type T, M2() The receiver is the value type *T, changing the Name value in both methods.

Declare a variable of type T and call it M1() and M2() .


 t1 := T{"t1"}
 fmt.Println("M1 Before the call: ", t1.Name)
 t1.M1()
 fmt.Println("M1 After the call: ", t1.Name)
 fmt.Println("M2 Before the call: ", t1.Name)
 t1.M2()
 fmt.Println("M2 After the call: ", t1.Name)

The output result is:

Before M1 is called: t1

After M1 is called: t1

M2 before the call: t1

After M2 is called: name2

Let's guess what go will do next.

Let's start with convention 1: The receiver can be thought of as the first argument of the function, which reads as follows: func M1(t T) , func M2(t *T) . go is not an object-oriented language, so understanding it with a syntax that looks object-oriented can be misleading.

When calling t1.M1() When the equivalent of M1(t1) , argument and row arguments of type T are acceptable. At this point in M1() t is just a copy of the value of t1, so M1() Changes to t1 will not be affected.

When calling M2() 0 , this is the T type passed to *T type, go may take the address of t1 to pass in: M2(&t1) . so M2() Can affect t1.

Both methods are owned.

Declare a variable of type *T and call it M1() and M2() .


 t2 := &T{"t2"}
 fmt.Println("M1 Before the call: ", t2.Name)
 t2.M1()
 fmt.Println("M1 After the call: ", t2.Name)
 fmt.Println("M2 Before the call: ", t2.Name)
 t2.M2()
 fmt.Println("M2 After the call: ", t2.Name)

The output result is:

M1 before call: t2

After M1 is called: t2

M2 before invocation: t2

After M2 is called: name2

t2.M1() => M1(t2) , t2 is pointer type, take the value of t2 and copy one copy to M1.

t2.M2() => M2(t2) , are pointer types, do not need to convert.

* A variable of type T also has both methods.

What happens if you pass it to the interface?

Declare 1 interface first


type Intf interface {
 M1()
 M2()
}

Use:


 var t1 T = T{"t1"}
 t1.M1()
 t1.M2()
 var t2 Intf = t1
 t2.M1()
 t2.M2()

Error:

./main.go:9: cannot use t1 (type T) as type Intf in assignment:


T does not implement Intf (M2 method has pointer receiver)

var t2 Intf = t1 This line gives an error.

t1 is M2() But why doesn't it pass to t2?

Simply put, according to interface theory: objects passing [to assignment] must implement the methods required by the interface, and t1 does not M2() , t1 pointer is implemented M2() . In addition, as in c, the function name itself is a pointer

When the var t2 Intf = t1 Modified to var t2 Intf = &t1 , t2 gets the address of t1, t2.M2() The modification can affect t1 now.

If you declare 1 method fun c f(t Intf) , the passing of parameters and the direct assignment above are the same case.

Nested types

Declare a type S and embed T


type S struct { T }

Test 1 using the following example:


 t1 := T{"t1"} 
 s := S{t1} 
 fmt.Println("M1 Before the call: ", s.Name) 
 s.M1() 
 fmt.Println("M1 After the call: ", s.Name) 
 fmt.Println("M2 Before the call: ", s.Name) 
 s.M2() 
 fmt.Println("M2 After the call: ", s.Name) 
 fmt.Println(t1.Name)

Output:

M1 before invocation: t1

After M1 is called: t1

Before M2 is called: t1

After M2 is called: name2

t1

By embedding T into S, the method and property owned by T are also owned by S, but the receiver is not S but T.

so s.M1() The equivalent of M1(t1) Rather than M1(s) .

Finally, the value of t1 has not changed, because we embedded T type, so S{t1} made a copy of t1.

What if we assigned s to the Intf interface?


 var intf Intf = s 
 intf.M1() 
 intf.M2()

Error:

cannot use s (type S) as type Intf in assignment: S does not implement Intf (M2 method has pointer receiver)

or M2() s is still a value type.

M2()1 In this case the compilation passes if in M2()2 Where the value of Name is changed, s.Name Has been changed, but t1.Name It's still the same, because t1 and s are no longer connected.

Try embedding *T below:


type S struct { *T }

When used like this:


 t1 := T{"t1"}
 fmt.Println("M1 Before the call: ", t1.Name)
 t1.M1()
 fmt.Println("M1 After the call: ", t1.Name)
 fmt.Println("M2 Before the call: ", t1.Name)
 t1.M2()
 fmt.Println("M2 After the call: ", t1.Name)
0

Before M1 is called: t1

After M1 is called: t1

M2 before call: t1

M2 called: name2

name2

The only difference is that the value of t1 changes at the end because we copied the pointer.

Then try assigning values to the interface:


 t1 := T{"t1"}
 fmt.Println("M1 Before the call: ", t1.Name)
 t1.M1()
 fmt.Println("M1 After the call: ", t1.Name)
 fmt.Println("M2 Before the call: ", t1.Name)
 t1.M2()
 fmt.Println("M2 After the call: ", t1.Name)
1

Compile without error. What we pass to intf here is a value type instead of a pointer. Why does it pass?

T is pointer type when copying s, so call M2() I'm passing in a pointer.

M2()1 The effect is the same as above.

conclusion


Related articles: