[GO]Some notes on Receivers 作者: rin 时间: August 17, 2023 分类: Go 评论 # Review of receivers When a value receiver is defined, Go automatically generates a corresponding pointer receiver. Auto-generating a pointer receiver causes differences in the method sets of pointer types and value types. For example, take the `Len()` method below, Go will automatically generate a corresponding pointer receiver version: ```go type List []int func (l List) Len() int { return len(l) } // func (l *List) Len() int { ... } func (l *List) Append(val int) { *l = append(*l, val) } ``` For type `List`, its method set includes: - `Len() int` For type `*List`, the method set includes: - `Len() int` - `Append(int)` This difference affects the use of interfaces: ```go type Container interface { Len() int Append(int) } var _ Container = List{} // no var _ Container = &List{} ``` However, for the type's own pointer and value, there appears to be no difference, both can call the two methods: ```go l := List{} pl := &List{} // List only implements `Len` l.Append(42) // how does Go find `Append`? // (&l).Append(42) l.Len() // gets 1 // *List implements `Append` explicitly, `Len` implicitly pl.Append(6) pl.Len() // gets 1 ``` In the `List` method set, there is no `Append(int)`. Go converts the corresponding call to `(&l).Append(42)` to implement this which is a syntactic sugar. However, if you cannot get the address of an object of a certain type, this feature does not work. Non-addressable types include `map` and `interface` and some other, as shown below: ```go m := map[string]List{"list1": List{}} p := &m["list1"] // cannot take the address of m["list1"] (map index expression of type List) var i interface{} = List{} p = &i.(List) // cannot take the address of i.(List) ``` Because the map stores `List`, you can directly call `List.Len()`. Since you cannot take the address of a map element, you cannot convert it to call `*List.Append()`. The same applies to `interface`: ```go fmt.Println(m["list1"].Len()) // ok m["list1"].Append(42) // bad, cannot call the pointer method Append on List i.(List).Len() // ok i.(List).Append() // bad, cannot call the pointer method Append on List ``` # What Do Auto-Generated Pointer Receivers Do If the auto-generated pointer receiver's function body is the same as the value receiver, can I modify the object's value through the pointer? No. In fact, the pointer receiver calls the value receiver and passes arguments by value. When using `dlv debug main.go` to debug `main.(*List).Len`, I found that it doesn't stop at this function breakpoint. Reviewing the previous code, `pl.Len()` may have been converted to `(*pl).Len()`, which caused it to use the value receiver. Therefore, it is necessary to forcefully use the pointer receiver: ```go l := List{} pl := &List{} // List only implements `Len` l.Append(42) // how does Go find `Append`? // (&l).Append(42) l.Len() // gets 1 // *List implements `Append` explicitly, `Len` implicitly pl.Append(6) pl.Len() // gets 1 // Forcefully call `func(l *List) Len()` (*List).Len(pl) // gets 1 ``` It is confirmed that `main.(*List).Len` internally calls `main.List.Len`. ![](http://static.lo-li.net/images/2023/10/2023-10-20_19-44.png) ![](http://static.lo-li.net/images/2023/10/2023-10-20_19-45.png)