The difference between Go struct (structure) value receiver and pointer receiver

For the structure

Methods can add new behaviors to user-defined types (structures). The difference between it and a function is that a method has a receiver . Add a receiver to a function, then it becomes a method. The receiver can be a value receiver or a pointer receiver .

When calling methods:
value types can call either the method of the value receiver or the method of the pointer receiver; the
pointer type can call the method of the pointer receiver or the method of the value receiver.
In other words, regardless of the type of the receiver of the method, the value and pointer of that type can be called, and it does not have to strictly conform to the type of the receiver.

Look at an example:

package main

import "fmt"

type Person struct {
    
    
    age int
}

func (p Person) howOld() int {
    
    
    return p.age
}

func (p *Person) growUp() {
    
    
    p.age += 1
}

func main() {
    
    
    // qcrao 是值类型
    qcrao := Person{
    
    age: 18}

    // 值类型 调用接收者也是值类型的方法
    fmt.Println(qcrao.howOld())

    // 值类型 调用接收者是指针类型的方法
    qcrao.growUp()
    fmt.Println(qcrao.howOld())

    // ----------------------

    // stefno 是指针类型
    stefno := &Person{
    
    age: 100}

    // 指针类型 调用接收者是值类型的方法
    fmt.Println(stefno.howOld())

    // 指针类型 调用接收者也是指针类型的方法
    stefno.growUp()
    fmt.Println(stefno.howOld())
}

The output of the above example is:

18
19
100
101

After calling the growUp function, no matter whether the caller is a value type or a pointer type, its Age value changes.

In fact, when the type and the receiver type of the method are different, the compiler actually does some work behind the scenes and presents it in a table:

For the interface

Conclusion: The
realization of the interface method where the receiver is a value type is equivalent to the automatic realization of the method where the receiver is a pointer type; the
realization of the interface method where the receiver is a pointer type will not automatically generate a method corresponding to the receiver as a value type.

Looking at another example, you will fully understand:

package main

import "fmt"

type coder interface {
    
    
    code()
    debug()
}

type Gopher struct {
    
    
    language string
}

func (p Gopher) code() {
    
    
    fmt.Printf("I am coding %s language\n", p.language)
}

func (p *Gopher) debug() {
    
    
    fmt.Printf("I am debuging %s language\n", p.language)
}

func main() {
    
    
    var c coder = &Gopher{
    
    "Go"}
    c.code()
    c.debug()
}

The above code defines an interface coder, which defines two functions: code() debug()
then defines a structure Gopher, which implements two methods, a value receiver and a pointer receiver .
Finally, we called the two functions defined in the main function through the interface type variables.

Run it, the result:

I am coding Go language
I am debuging Go language

But if we change the first statement of the main function:

func main() {
    
    
    var c coder = Gopher{
    
    "Go"}
    c.code()
    c.debug()
}

Run it and report an error:

src/main.go:23:6: cannot use Gopher literal (type Gopher) as type coder in assignment:
    Gopher does not implement coder (debug method has pointer receiver)

Do you see the difference between the two codes?

The first time was to assign &Gopher to the coder; the second time to assign Gopher to the coder.

The second error was that Gopher did not implement a coder. Obviously, because the Gopher type does not implement the debug method; on the surface, the *Gopher type does not implement the code method, but because the Gopher type implements the code method, the *Gopher type automatically has the code method.

Of course, there is a simple explanation for the above statement: the receiver is a pointer type method, and it is very likely that the receiver’s attributes will be changed in the method, thereby affecting the receiver; for the receiver is a value type method, in The method does not affect the receiver itself.
Therefore, when you implement a method whose receiver is a value type, you can automatically generate a method whose receiver is a pointer type, because neither will affect the receiver. However, when a method whose receiver is a pointer type is implemented, if a method whose receiver is a value type is automatically generated at this time, the originally expected change to the receiver (implemented through pointers) cannot be achieved now, because the value type will produce A copy does not really affect the caller.

Finally, just remember the following points:

If you implement an interface method whose receiver is a value type, you will implicitly also implement a method whose receiver is a pointer type.

When are the two used

If the receiver of the method is a value type, whether the caller is an object or an object pointer, the modification is a copy of the object and does not affect the caller; if the receiver of the method is a pointer type, the caller modifies the object pointed to by the pointer itself.

The reason for using pointers as the receiver of a method: A
method can modify the value pointed to by the receiver. Avoid copying the value every time the method is called. This is more efficient when the value type is a large structure.

The value receiver or the pointer receiver is not determined by whether the method modifies the caller (that is, the receiver), but should be based on the nature of the type.

If the type has "primitive nature", that is to say, its members are all primitive types built into the Go language, such as strings, integer values, etc., then define the method of the value receiver type. Like the built-in reference types, such as slice, map, interface, and channel, these types are special. When declaring them, a header is actually created. For them, it is also a way to directly define the value receiver type. In this way, when calling the function, these types of headers are directly copied, and the header itself is designed for copying.

If the type has a non-primitive nature and cannot be safely copied, this type should always be shared, then define the pointer receiver method. For example, the struct File in the go source code should not be copied, there should be only one entity.

If you want to learn more Go language grammar and common knowledge points of Go language at work, you can refer to my note source code https://github.com/qiuyunzhao/go_basis

Guess you like

Origin blog.csdn.net/QiuHaoqian/article/details/107861222