Given the code
synatx = "proto3";
package test;
message Password {
string hash = 1;
string salt = 2;
}
message User {
Password password = 1;
}
The generated User struct contains than a pointer to a password struct. Is it possible to embed the struct? The User would contain the Password (without a pointer) and the Password would be initialized along with the User.
This isn't possible with the current code generator. Protobuf schemas are independent of language, so it would be difficult to specify the semantics you want in a language-neutral way.
I would be fine with a language-specific option. Something like Password password = 1 [go_embed=true];.
what sort of use case would this be used for @phifty?
At least to provide automatic binding with chi.Render
sendRequest := &mailer.SendRequest{
Sender: &mailer.Sender{},
Mail: &mailer.Mail{},
}
if err := render.Bind(r, sendRequest); err != nil {}
I wrote the plugin to support struct embedding: https://github.com/golang/protobuf/pull/430
Adding support for embedding in the mainline proto repo breaks the semantics of protobufs. According to the proto language spec, there is a distinction between the zero value for a message and an unset message (even for proto3). Embedding provides no such distinctions.
We're going to reject this for the following reasons:
1) Embedded values violates the semantics of protos.
It is unclear in this proposal whether the OP intended for Password to be embedded or *Password. If the former, then we have to reject this proposal since it violates the semantics of zero-vs-unset messages in protos. I spoke to the protobuf team and we will maintain a hard stance on that requirement.
2) Embedded pointers lead to brittle code.
Suppose that the intended behavior was the later (i.e., *Password), then this leads to brittle code as it is trivial for someone to do User.Salt without realizing that this can result in a nil-pointer derefence on User.Password, which isn't visible in the expression. It's annoying enough with Foo.Bar where you need to consciously think whether Foo is nil or not. It's even worse when you need to think about whether some intermediate de-reference that is not even visible may be nil.
3) Embedding forwards unintended methods.
When you embed T, all of the methods of T are forwarded to the parent. This is often problematic. For example, #256 and #210 proposes that users to specify whether magic methods like MarshalJSON should be generated on a per-message basis. Those two proposals have a much higher chance of being accepted. However, they would interact badly with this since T.MarshalJSON would get forwarded to the Parent if the Parent did not define its own MarshalJSON method. This is almost certainly not the desired behavior.
4) Embedding in Go creates portability issues over time. Imagine the following:
type Parent struct {
Username
other.pkg.Bar
}
type Username struct { Name, Pass string }
var p Parent
p.Name = "dsnet"
This may work fine today, but can run into compilation issue when the Bar type (which is outside your control) adds a Name field. For this reason, the Go1 compatibility agreement actually recommends against embedded types.
Most helpful comment
I would be fine with a language-specific option. Something like
Password password = 1 [go_embed=true];.