1
头图

Friendly reminder: This article takes about 3 minutes and 49 seconds to read. Please advise us on the shortcomings. Thank you for reading. Subscribe to this site

Original address of translation: Should methods be declared on T or *T – David

In Go, for any type T, there is a type *T, which is the result of an expression that receives the type T, for example:

type T struct { a int; b bool } 
var t T    // t's type is T 
var p = &t // p's type is *T

These two types, T and *T are different, but *T cannot replace T.

You can declare a method on any type you own; that is, the type declared by the function in your package. Therefore, you can declare methods on the declared type T and the corresponding derived pointer type *T. Another way of saying that is that a method on a type is declared as a copy of the receiver's receiver value, or a pointer to its receiver's value. So the question remains, which form is the most appropriate?

Obviously, if your method changes his receiver, he should declare it on *T. However, if the method does not change its receiver, is it safe to declare it on T?

Facts have proved that the security situation is very limited if you do this (a simple understanding is insecure). For example, it is well known that you should not copy the value of a sync.Mutex because it breaks the invariant of the mutex. Since mutexes control access to variables (shared resources), they are often wrapped in a structure that contains their controlled values (shared resources):

package counter

type Val struct {
    mu  sync.Mutex
    val int
}

func (v *Val) Get() int {
    v.mu.Lock()
    defer v.mu.Unlock()
    return v.val
}

func (v *Val) Add(n int) {
    v.mu.Lock()
    defer v.mu.Unlock()
    v.val += n
}

Most Gopher knows that it is wrong to forget to declare the Get or Add method on the pointer receiver *Val. However, any type that embeds Val to take advantage of its 0 value must also only declare the method on its pointer receiver, otherwise it may unintentionally copy the content of its embedded type value:

type Stats struct {
    a, b, c counter.Val
}

func (s Stats) Sum() int {
    return s.a.Get() + s.b.Get() + s.c.Get() // whoops(哎呀)
}

Maintaining the type of value slice may have similar traps, and of course there may be unexpected data competition.

In short, I think you should prefer to declare methods on *T, unless you have a very good reason not to do so.

  1. We say T but this is just a placeholder for the type you declared;
  2. This rule is recursive, taken *T address type variable returns **T type result;
  3. This is why no one can declare methods on basic types like int;
  4. The methods in Go are just syntactic sugar for functions that pass the receiver as the first formal parameter;
  5. If the method does not change its receiver, does it need to be a method?

related articles:

  1. What is the zero value, and why is it useful?
  2. Ice cream makers and data races
  3. Slices from the ground up
  4. The empty struct

Finally, this article is my first attempt to translate an English article. Although my English level is not very good and some words are not known, I believe that translating an article by myself can learn English and understand the Go design to get the fun of double.
image.png


Meng小羽
199 声望629 粉丝

你好,我是 Meng小羽,非标准程序猿,喜欢编码、摄影、音乐、吉他,对新鲜事物和新的技术总是保持着好奇心并愿意去探索。