dongye7231 2017-01-05 05:05
浏览 90
已采纳

当命名类型为T的任何方法具有指针接收者时,复制类型T的实例

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)

  • 写回答

1条回答 默认 最新

  • dongyan5706 2017-01-05 10:07
    关注

    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
    ", 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
    ", 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
    ", 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
    ", 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).

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥15 模电中二极管,三极管和电容的应用
  • ¥15 关于模型导入UNITY的.FBX: Check external application preferences.警告。
  • ¥15 气象网格数据与卫星轨道数据如何匹配
  • ¥100 java ee ssm项目 悬赏,感兴趣直接联系我
  • ¥15 微软账户问题不小心注销了好像
  • ¥15 x264库中预测模式字IPM、运动向量差MVD、量化后的DCT系数的位置
  • ¥15 curl 命令调用正常,程序调用报 java.net.ConnectException: connection refused
  • ¥20 关于web前端如何播放二次加密m3u8视频的问题
  • ¥15 使用百度地图api 位置函数报错?
  • ¥15 metamask如何添加TRON自定义网络