当任何一个命名类型T的方法都有一个指针接收器时,复制T类型的实例 [英] Copy instances of type T, when any of the methods of a named type T have a pointer receiver

查看:128
本文介绍了当任何一个命名类型T的方法都有一个指针接收器时,复制T类型的实例的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我最近阅读了 Go编程语言书,这是学习golang编程语言的好资源。
在6.2节中有一段关于 T 类型的拷贝实例,当它是指针接收器或者不在方法中时,我无法理解它。
有没有解释这个段落有一个有意义的例子?


6.2使用指针接收器的方法



如果命名类型T的所有方法都有T本身的接收器类型(非* T),则复制该类型的实例是安全的;调用它的任何方法都必须复制。例如,time.Duration值被自由地复制,包括作为函数的参数。但是如果任何方法都有指针接收器,则应避免复制T的实例,因为这样做可能会违反内部不变量。例如,复制bytes.Buffer的一个实例会导致原始文件和副本到别名(§2.3.2)相同的底层字节数组。后续的方法调用会产生不可预知的影响。



(转到编程语言Alan AA Donovan·Brian W. Kernighan)

$当调用一个方法时,该方法被调用的值首先被复制,并且该副本被传递/用作接收者。

>

如果一个类型只包含具有值接收者的方法,这意味着无论内部使用什么方法,也无论您(或其他人)调用什么方法,方法都不会能够更改原始 值,因为 - 如上所述 - 只传递一个副本,并且该方法只能修改副本 - 而不是原始的。



因此,这意味着如果您复制该值,则不必担心,在原始或副本上调用的方法都不能/不会修改该值(s)。



不是,类型具有带指针接收器的方法。如果一个方法有一个指针接收器,该方法可以改变/修改指向的值,这不是一个副本,它是原始值(只有指针是一个副本,但它指向原始值)。



我们来看一个例子。我们创建了一个 int 封装类型,它有两个字段:一个 int 和一个 * int 。我们打算在两个字段中存储相同的数字,但其中一个是指针(并且我们将 int 存储在指向的值中): p>

  type Wrapper struct {
v int
p * int
}

)是一样的,我们提供了一个 Set()方法,它同时设置:

< pre $ func(w * Wrapper)Set(v int){
wv = v
* wp = v
}

Wrapper.Set()有一个指针接收器( * Wrapper ),因为它必须修改该值(其类型为 Wrapper )。无论我们传递给 Set()的号码是多少,我们都可以确定一旦 Set()返回, code> v 和 * p 将会是相同的,并且等于传递给 Set()



现在,如果我们具有 Wrapper 的值:

  a:= Wrapper {v:0,p:new(int)} 

我们可以调用 Set()方法:

  a.Set(1)

编译器将自动将 a 的地址用作接收者,所以上面的代码表示(& a).Set(1)

我们预计类型 Wrapper 的任何值都具有存储在 Wrapper.v * Wrapper.pv ,如果只有 Set()方法来更改字段的值。

现在,让我们看看如果我们复制 a

  a:=包装器{v:0,p:new(int)} 
b:= a
fmt.Printf(a.v =%d,a.p =%d; bv =%d,bp =%d \ n,av,* ap,bv,* bp)

a.Set(1)
fmt.Printf(av =% d,ap =%d; bv =%d,bp =%d \ n,av,* ap,bv,* bp)

输出(在去游乐场试试):

  av = 0,ap = 0; bv = 0,bp = 0 
av = 1,ap = 1; bv = 0,bp = 1

我们复制了一个(存储在 b 中),然后打印这些值,到目前为止我们称之为 a.Set (1),之后 a 仍然不错,但 b 的内部状态变成无效: bv 不等于 * bp 解释非常清楚:当我们复制 a (它是一个 struct 类型),它复制其字段的值(包括指针 ),并且 b 中的指针将指向与 a 。H因为修改指向的值会影响 Wrapper 的两个副本,但我们有2个不同的 v 字段如果你有指针接收方法,你应该使用指针值。



请注意,如果您要复制 * Wrapper 的值,那么一切都会很酷:

  a:=& Wrapper {v:0,p:new(int)} 
b:= a
fmt.Printf(av =%d,ap =%d; bv =%d,bp =%d \ n,av,* ap,bv,* bp)

a.Set(1)
fmt.Printf(av =% d,ap =%d; bv =%d,bp =%d \ n,av,* ap,bv,* bp)

输出(在 Go Playground 上试用):

  av = 0,ap = 0; bv = 0,bp = 0 
av = 1,ap = 1 ; bv = 1,bp = 1

在这种情况下 a 是一个指针,它是 * Wrapper 类型的一个指针,我们拷贝了它(存储在 b ),称为 a.Set(),并且 a b 仍然有效,这里我们只有一个 Wrapper 值, a 只包含一个指针(它的地址)当我们复制 a 时,我们只复制指针值的副本,而不是 struct value(类型 Wrapper )。


I read The Go Programming Language Book recently, the good resource for learning golang programming language. There is a paragraph in 6.2 section about copy instance of type T when it is pointer receiver or not in methods, that I can't understand it. Is there any that explain this paragraph with a meaningful example?

6.2 Methods with a Pointer Receiver

If all the methods of a named type T have a receiver type of T itself (not *T ), it is safe to copy instances of that type; calling any of its methods necessarily makes a copy. For example, time.Duration values are liberally copied, including as arguments to functions. But if any method has a pointer receiver, you should avoid copying instances of T because doing so may violate internal invariants. For example, copying an instance of bytes.Buffer would cause the original and the copy to alias ( §2.3.2 ) the same underlying array of bytes. Subsequent method calls would have unpredictable effects.

(The Go Programming Language Alan A. A. Donovan · Brian W. Kernighan)

解决方案

When calling a method, the value the method is called on is first copied, and that copy is passed / used as the receiver.

If a type only has methods with value receivers, that means no matter what the methods do inside, and no matter what methods you (or anyone else) call, the methods won't be able to change the original value, because –as noted above– only a copy is passed and the method could only modify the copy – and not he original.

So this means if you copy the value, you don't have to worry, neither the methods called on the original nor on the copy can't / won't modify the value(s).

Not, when the type has methods with pointer receivers. If a method has a pointer receiver, the method can change / modify the pointed value, which is not a copy, it's the original value (only the pointer is a copy but it points to the original value).

Let's see an example. We create an int wrapper type, which has 2 fields: an int and an *int. We intend to store the same number in both fields, but one is a pointer (and we store the int in the pointed value):

type Wrapper struct {
    v int
    p *int
}

To make sure both values (v and *p) are the same, we provide a Set() method, which sets both:

func (w *Wrapper) Set(v int) {
    w.v = v
    *w.p = v
}

Wrapper.Set() has a pointer receiver (*Wrapper) as it has to modify the value (which is of type Wrapper). No matter what number we pass to Set(), we can be sure that once Set() returns, both v and *p will be the same, and equal to the number passed to Set().

Now if we have a value of Wrapper:

a := Wrapper{v: 0, p: new(int)}

We can call the Set() method on it:

a.Set(1)

The compiler will automatically take the address of a to use as the receiver, so the above code means (&a).Set(1).

We'd expect that any value of type Wrapper has the same number stored in Wrapper.v and *Wrapper.pv, if only the Set() method is used to change the fields' values.

Now let's see the problem if we make a copy of a:

a := Wrapper{v: 0, p: new(int)}
b := a
fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

a.Set(1)
fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

Output (try it on the Go Playground):

a.v=0, a.p=0;  b.v=0, b.p=0
a.v=1, a.p=1;  b.v=0, b.p=1

We made a copy of a (stored it in b), and printed the values. So far so good. Then we called a.Set(1), after which a is still good, but internal state of b became invalid: b.v does not equal to *b.p anymore. The explanation is quite clear: when we made a copy of a (which is a struct type), that copies the values of its fields (including the pointer p), and the pointer in b will point to the same value as the pointer in a. Hence modifying the pointed value will affect both copies of Wrapper, but we have 2 distinct v fields (they are non-pointers).

If you have methods with pointer receivers, you should work with pointer values.

Note that if you would copy a value of *Wrapper, everything would still be cool:

a := &Wrapper{v: 0, p: new(int)}
b := a
fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

a.Set(1)
fmt.Printf("a.v=%d, a.p=%d;  b.v=%d, b.p=%d\n", a.v, *a.p, b.v, *b.p)

Output (try it on the Go Playground):

a.v=0, a.p=0;  b.v=0, b.p=0
a.v=1, a.p=1;  b.v=1, b.p=1

In this case a is a pointer, it's of type *Wrapper. We made a copy of it (stored it in b), called a.Set(), and both the internal state of a and b remained valid. Here we only have one Wrapper value, a only holds a pointer to it (its address). When we copy a, we only make a copy of the pointer value, and not the struct value (of type Wrapper).

这篇关于当任何一个命名类型T的方法都有一个指针接收器时,复制T类型的实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆