Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Why has methods are not generated using protoc 3.17.* if in protobuf repo they say it should? #1336

Closed
frederikhors opened this issue Jul 2, 2021 · 7 comments

Comments

@frederikhors
Copy link

Reading here: https://github.com/protocolbuffers/protobuf/blob/master/docs/field_presence.md#go-example the Golang example:

m := GetProto()
if (m.HasFoo()) {
  // Clear the field:
  m.Foo = nil
} else {
  // Field is not present, so set it.
  m.Foo = proto.Int32(1);
}

if I use:

protoc pkg/user.proto --go_out=. --go_opt=module=my_app --go-grpc_out=. --go-grpc_opt=module=my_app

with:

syntax = "proto3";

package example;

message MyPlayer {
  uint64 id = 1;
  optional string description = 2;
  uint32 qty = 3;
  optional uint64 age = 4;
}

it doesn't generate any has<T>() method.

Why?

// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// 	protoc-gen-go v1.26.0
// 	protoc        v3.17.3

Am I wrong if I use MyPlayer generated proto fields instead of methods?

Example:

if MyPlayer.description != nil {
  description = *MyPlayer.description
}

instead of

description = MyPlayer.GetDescription()

which is not what I want (I want to detect nil values).

@puellanivis
Copy link
Collaborator

The proto.Int32(1) tells me that that example is using the syntax for the proto2 implementation, which has presence detection, and thus HasField functions.

In the proto3 standard, a non-present field is equivalent to a zero value which is equivalent to a non-present field. (Some exceptions apply, e.g. an empty Message is not the same as a not-appearing Message.)

I also see that you have the optional keyword, which is being rolled out, and in that case, the Description field will indeed be a pointer to string, and a nil value will indicate not present, and "" would indicate present but empty. But there is no reason to specifically that a field is optional if the zero value is not a valid value. Non-appearance will give it the zero value, and you can just test if MPLayer.description != "" { … }. Making fields unnecessarily optional when their zero value is an invalid value will only put pressure on your garbage collector as you have to allocate standalone values to use as pointers in the message’s struct body.

@frederikhors
Copy link
Author

They talk about proto3, maybe they are wrong?

You're right. But my question is if I'm using it well or am I wrong (if empty is valid value)?

@neild
Copy link
Contributor

neild commented Jul 2, 2021

That doc is wrong. There are no Has methods in the generated code. Test for presence by comparing fields to nil.

Rewriting those examples correctly:

// Field foo does not have presence.
// If field foo is not 0, set it to 0.
// If field foo is 0, set it to 1.
m := GetProto()
if m.Foo != 0 {
  // "Clear" the field:
  m.Foo = 0
} else {
  // Default value: field may not have been present.
  m.Foo = 1
}
// Field foo has presence.
// If foo is set, clear it.
// If foo is not set, set it to 1.
m := GetProto()
if m.Foo != nil {
  // Clear the field:
  m.Foo = nil
} else {
  // Field is not present, so set it.
  m.Foo = proto.Int32(1)
}

@dsnet
Copy link
Member

dsnet commented Jul 2, 2021

The documentation on https://github.com/protocolbuffers/protobuf/blob/master/docs/field_presence.md#go-example is incorrect. Using "optional" in proto3 makes the field be generated just as it would in proto2.

@neild
Copy link
Contributor

neild commented Jul 2, 2021

PR to fix that doc:
protocolbuffers/protobuf#8788

@puellanivis
Copy link
Collaborator

puellanivis commented Jul 2, 2021

They talk about proto3, maybe they are wrong?

Ick, I didn’t even notice the other minor go fmt stuff in the example code. I had just assumed the example had been valid at some point, and tried to explain why it might now work now. 🤦‍♀️ I guess that’s what I get with assumptions…

As the others noted the example code was wrong; and after looking at the history of the file and the PR itself, it looks like the original was itself an assumption on how the Go code would work.

@frederikhors
Copy link
Author

What beautiful people you are! Thanks for all the advice!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants