golang / protobuf

Go support for Google's protocol buffers
BSD 3-Clause "New" or "Revised" License
9.66k stars 1.58k forks source link

unexpected behavir of proto.Clone #1470

Closed finelog closed 1 year ago

finelog commented 1 year ago

I'm not sure if this is a bug, and I don't see others have reported this An unexpected behavir occur to me when I was using the proto.Clone method with oneof field, if an oneof field is nil, after proto.Clone, the field in new message is not nil this is kind weird... take me sometime found out while debug my progroam this morning.

anyway, I have put the code to reproduce behavir described above, please check it out, thanks!

What version of protobuf and what language are you using? Version: v1.28.0

What did you do? pbhelper.proto:

syntax = "proto3";

package pbhelper;

message IntVal {
    string key = 1;
    uint64 val = 2;
}

message StrVal {
    string key = 1;
    string val = 2;
}

message TestClone {
    oneof value_oneof {
      IntVal int_val = 1;
      StrVal str_val = 2;
    }
}

pbhelper.go

package pbhelper

import "google.golang.org/protobuf/proto"

func Clone(m proto.Message) proto.Message {
    if m != nil {
        return proto.Clone(m)
    }
    return nil
}

main.go

package main

import (
    "fmt"

    proto "path/to/above/pbhelper"
)

func main() {
    mixVal := &proto.TestClone{
        ValueOneof: &proto.TestClone_IntVal{},
    }
    fmt.Printf("%#v\n", mixVal.ValueOneof.(*proto.TestClone_IntVal))
    mixVal = proto.Clone(mixVal).(*proto.TestClone)
    fmt.Printf("%#v\n", mixVal.ValueOneof.(*proto.TestClone_IntVal))
}

What did you expect to see?

&proto.TestClone_IntVal{IntVal:(*proto.IntVal)(nil)}
&proto.TestClone_IntVal{IntVal:(*proto.IntVal)(nil)}

What did you see instead?

&proto.TestClone_IntVal{IntVal:(*proto.IntVal)(nil)}
&proto.TestClone_IntVal{IntVal:(*proto.IntVal)(0xc000484000)}

Make sure you include information that can help us debug (full error message, exception listing, stack trace, logs).

Anything else we should know about your project / environment? protoc version: v3.6.1

go env

GO111MODULE="auto"
GOARCH="amd64"
GOBIN=""
GOCACHE="/home/xxx/.cache/go-build"
GOENV="/home/xxx/.config/go/env"
GOEXE=""
GOEXPERIMENT=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GOINSECURE=""
GOMODCACHE="/home/xxx/go/pkg/mod"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/home/xx/go"
GOPRIVATE="xxx.xxx.com"
GOPROXY="https://goproxy.cn,direct"
GOROOT="/home/xxx/go/go1.18"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/home/xxx/go/go1.18/pkg/tool/linux_amd64"
GOVCS=""
GOVERSION="go1.18"
GCCGO="gccgo"
GOAMD64="v1"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/home/xxx/go/xxx/go.mod"
GOWORK=""
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build242619372=/tmp/go-build -gno-record-gcc-switches"
puellanivis commented 1 year ago

This is happening because you’re specifying that the oneof has a type of IntVal, even if you’re not setting the IntVal value, and a oneof can only have a type, if a value is actually specified. If you think about it, how could it tell from the protowire format that there was an IntVal type in the oneof? The only way to represent this situation is with a zero-value IntVal.

Compare the output of your code to this:

package main

import (
        "fmt"

        proto "github.com/puellanivis/clone-proto/proto"
)

func main() {
        mixVal := &proto.TestClone{
                ValueOneof: &proto.TestClone_IntVal{},
        }
        fmt.Printf("%#v, %#v\n", mixVal.GetIntVal().GetKey(), mixVal.GetIntVal().GetVal())
        mixVal = proto.Clone(mixVal).(*proto.TestClone)
        fmt.Printf("%#v, %#v\n", mixVal.GetIntVal().GetKey(), mixVal.GetIntVal().GetVal())
}
finelog commented 1 year ago

I think I can see the point, beause my view is based on the go type system, but in the protobuf's view, such behavir is expected, I will close this then. thanks for the explanation! @puellanivis