golang gob将指针转换为0变成nil指针。

huangapple go评论68阅读模式
英文:

golang gob converts pointer to 0 into nil pointer

问题

我正在尝试使用Go的net/rpc包发送数据结构。数据结构包括一个指向uint64的指针。该指针永远不会为nil,但其值可能为0。我发现当值为0时,接收方会看到一个nil指针。当值为非0时,接收方会看到一个非nil指针,指向正确的值。这是有问题的,因为这意味着RPC违反了我的数据结构的不变性:指针永远不会为nil。

我在这里有一个演示此行为的Go Playground链接:https://play.golang.org/p/Un3bTe5F-P

package main

import (
    "bytes"
    "encoding/gob"
    "fmt"
    "log"
)

type P struct {
    Zero, One int
    Ptr       *int
}

func main() {
    // 初始化编码器和解码器。通常,enc和dec将绑定到网络连接,并且编码器和解码器将在不同的进程中运行。
    var network bytes.Buffer        // 用于模拟网络连接
    enc := gob.NewEncoder(&network) // 将写入network
    dec := gob.NewDecoder(&network) // 将从network读取

    // 编码(发送)值
    var p P
    p.Zero = 0
    p.One = 1
    p.Ptr = &p.Zero
    fmt.Printf("p0: %v\n", p)
    err := enc.Encode(p)
    if err != nil {
        log.Fatal("encode error:", err)
    }

    // 解码(接收)值
    var q P
    err = dec.Decode(&q)
    if err != nil {
        log.Fatal("decode error:", err)
    }
    fmt.Printf("q0: %v\n", q)

    p.Ptr = &p.One
    fmt.Printf("p1: %v\n", p)
    err = enc.Encode(p)
    if err != nil {
        log.Fatal("encode error:", err)
    }

    err = dec.Decode(&q)
    if err != nil {
        log.Fatal("decode error:", err)
    }
    fmt.Printf("q1: %v\n", q)
}

这段代码的输出是:

p0: {0 1 0x1050a780}
q0: {0 1 <nil>}
p1: {0 1 0x1050a784}
q1: {0 1 0x1050aba8}

因此,当Ptr指向0时,在接收方变为nil。当Ptr指向1时,正常传递。

这是一个bug吗?有没有解决这个问题的方法?我希望避免在接收方对我的数据结构进行反序列化以修复所有意外的nil指针...

英文:

I'm trying to use go's net/rpc package to send data structures. The data structure includes a pointer to uint64. The pointer is never nil, but the value may be 0. I'm finding that when the value is 0, the receiver sees a nil pointer. When the value is non-0, the receives sees a non-nil pointer that points to a proper value. This is problematic, because it means that the RPC is breaking an invariant of my data structure: the pointer will never be nil.

I have a go playground that demonstrates this behavior here: https://play.golang.org/p/Un3bTe5F-P

package main
import (
&quot;bytes&quot;
&quot;encoding/gob&quot;
&quot;fmt&quot;
&quot;log&quot;
)
type P struct {
Zero, One int
Ptr    *int
}
func main() {
// Initialize the encoder and decoder.  Normally enc and dec would be
// bound to network connections and the encoder and decoder would
// run in different processes.
var network bytes.Buffer        // Stand-in for a network connection
enc := gob.NewEncoder(&amp;network) // Will write to network.
dec := gob.NewDecoder(&amp;network) // Will read from network.
// Encode (send) the value.
var p P
p.Zero = 0
p.One = 1
p.Ptr = &amp;p.Zero
fmt.Printf(&quot;p0: %s\n&quot;, p)
err := enc.Encode(p)
if err != nil {
log.Fatal(&quot;encode error:&quot;, err)
}
// Decode (receive) the value.
var q P
err = dec.Decode(&amp;q)
if err != nil {
log.Fatal(&quot;decode error:&quot;, err)
}
fmt.Printf(&quot;q0: %s\n&quot;, q)
p.Ptr = &amp;p.One
fmt.Printf(&quot;p1: %s\n&quot;, p)
err = enc.Encode(p)
if err != nil {
log.Fatal(&quot;encode error:&quot;, err)
}
err = dec.Decode(&amp;q)
if err != nil {
log.Fatal(&quot;decode error:&quot;, err)
}
fmt.Printf(&quot;q1: %s\n&quot;, q)
}

The output from this code is:

p0: {%!s(int=0) %!s(int=1) %!s(*int=0x1050a780)}
q0: {%!s(int=0) %!s(int=1) %!s(*int=&lt;nil&gt;)}
p1: {%!s(int=0) %!s(int=1) %!s(*int=0x1050a784)}
q1: {%!s(int=0) %!s(int=1) %!s(*int=0x1050aba8)}

So when Ptr points to a 0, it becomes nil on the receiver side. When Ptr points to 1, it is passed through normally.

Is this a bug? Is there a way around this problem? I want to avoid having to unmarshall my detastructure on the receiver side to fix all the unexpected nil pointers...

答案1

得分: 3

这种行为是 gob 协议的一个限制,根据 2013 年提出的缺陷报告来看 - 请参阅 https://github.com/golang/go/issues/4609

请记住,gob 不发送指针,指针会被解引用并传递值。因此,当 p.Ptr 设置为 &p.One 时,你会发现 q.Ptr != &q.One。

英文:

This behaviour is a limitation of the gob protocol according the defect raised back in 2013 - see https://github.com/golang/go/issues/4609

Bear in mind that gob doesn't send pointers, the pointer is dereferenced and the value is passed. As such when the p.Ptr is set to &p.One, you'll find that q.Ptr != &q.One

huangapple
  • 本文由 发表于 2017年3月8日 23:14:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/42675054.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定