thurt
thurt

Reputation: 912

What's the purpose of ProtoMessage method?

when I generate go code from a protocol buffer file, I noticed that each of the generated structs implement the Message interface, https://github.com/golang/protobuf/blob/master/proto/lib.go#L277

For an example of the code that is generated, see https://github.com/google/go-genproto/blob/master/googleapis/rpc/status/status.pb.go#L97

Clearly, the other methods on Message interface, String() and Reset(), have an obvious purpose and the concrete implementation example makes that clear. However, I don't understand the purpose of the ProtoMessage() method. The method takes no parameters and returns no arguments, so why is it there?

Upvotes: 9

Views: 5555

Answers (1)

icza
icza

Reputation: 418745

Quoting from official doc: Protocol Buffers: Go Generated Code:

Given a simple message declaration:

message Foo {}

the protocol buffer compiler generates a struct called Foo. A *Foo implements the Message interface. See the inline comments for more information.

type Foo struct {
}

// Reset sets the proto's state to default values.
func (m *Foo) Reset()         { *m = Foo{} }

// String returns a string representation of the proto.
func (m *Foo) String() string { return proto.CompactTextString(m) }

// ProtoMessage acts as a tag to make sure no one accidentally implements the
// proto.Message interface.
func (*Foo) ProtoMessage()    {}

Note that all of these members are always present; the optimize_for option does not affect the output of the Go code generator.

This is a (similar) technique described in Go official FAQ: How can I guarantee my type satisfies an interface?

If you wish the users of an interface to explicitly declare that they implement it, you can add a method with a descriptive name to the interface's method set. For example:

type Fooer interface {
    Foo()
    ImplementsFooer()
}

A type must then implement the ImplementsFooer method to be a Fooer, clearly documenting the fact and announcing it in godoc's output.

type Bar struct{}
func (b Bar) ImplementsFooer() {}
func (b Bar) Foo() {}

Most code doesn't make use of such constraints, since they limit the utility of the interface idea. Sometimes, though, they're necessary to resolve ambiguities among similar interfaces.

So the ProtoMessage() method basically has 2 purposes:

  • The main purpose is as documented in the generated code: to make sure you don't pass a value for protobuf serialization / deserialization which otherwise (without this method) would implement the proto.Message interface but is not a "real" protobuf message value:

    ProtoMessage acts as a tag to make sure no one accidentally implements the proto.Message interface.

  • And it also documents the fact that the type is a (implements) Message. This may not have a real value at first as the code is generated and you should not bother much with it, but code analyzers and Go IDE's may make good use of it; and it also appears in generated docs.

Upvotes: 7

Related Questions