3

The problem:

I have 2 files in the root directory. I use Makefile to generate Go code from .proto files.

But the language field in the Video struct is a value not a pointer to the value. And the subtitles field in the Video struct is an array of values not an array of pointers to the value.

The question is:

How can I make protoc generate a pointer to the value?

video.pb.go

type Video struct {
    state protoimpl.MessageState
    sizeCache protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Id        string              `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"`
    Title     string              `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"`

    // I need *language.ISO639_1 below
    Languages language.ISO639_1   `protobuf:"varint,3,opt,name=languages,proto3,enum=language.ISO639_1" json:"languages,omitempty"`

    // I need []*language.ISO639_1 below
    Subtitles []language.ISO639_1 `protobuf:"varint,4,rep,packed,name=subtitles,proto3,enum=language.ISO639_1" json:"subtitles,omitempty"`
}

Makefile

gen:
   # Video
   protoc -I. --go_out=plugins=grpc,paths=source_relative:video video.proto

   # Language
   protoc -I. --go_out=plugins=grpc,paths=source_relative:language language.proto

language.proto

syntax = "proto3";

package language;

option go_package = "example.com/group/repo/language;language";

enum ISO639_1 {
    UNKNOWN = 0;
    zh      = 1;
}

video.proto

syntax = "proto3";

package video;

import "language.proto";

option go_package = "example.com/group/repo/video;video";

message Video {
             string            id        = 1;
             string            title     = 2;
             language.ISO639_1 language  = 3;
    repeated language.ISO639_1 subtitles = 4;
}

protoc version: libprotoc 3.11.4

Klimbo
  • 1,308
  • 3
  • 15
  • 34
  • @blackgreen Thank you. I have already seen it, but it is not the same. The problem here is that protoc doesn't generate pointers if proto files have different package names. If they are the same - it will be a pointer. – Klimbo Jun 18 '20 at 18:09
  • But I have a lot of places where I need language, so I decided to move it to separate proto file. But I have faced this strange behaviour – Klimbo Jun 18 '20 at 18:11
  • I am not really sure what you are trying to achieve. What good should a pointer be in a data exchange format? You are basically trying to say „Hey, the data I want to transmit is on the source machine, at 0x...!“ – Markus W Mahlberg Jun 20 '20 at 02:01

1 Answers1

1

Starting in proto3 version 3.12, the field presence feature is supported experimentally, meaning you can again use the optional keyword, similarly to proto2.

You can achieve this by passing a flag --experimental_allow_proto3_optional to protoc when generating the pb.go files (ensure you're running new enough version of protoc and protoc-gen-go to support this experimental feature).

So given this pseudo-.proto file:

enum ISO639_1 {
    UNKNOWN = 0;
    zh      = 1;
}

message Video {
    string            id        = 1;
    string            title     = 2;

    optional ISO639_1 language  = 3;
    repeated ISO639_1 subtitles = 4;
}

You should get generated struct with:

type Video struct {
    state         protoimpl.MessageState
    sizeCache     protoimpl.SizeCache
    unknownFields protoimpl.UnknownFields

    Language  *ISO639_1  `protobuf:"varint,3,opt,name=language,proto3,enum=grpctrace.ISO639_1,oneof" json:"language,omitempty"`
    Subtitles []ISO639_1 `protobuf:"varint,4,rep,packed,name=subtitles,proto3,enum=grpctrace.ISO639_1" json:"subtitles,omitempty"`
}

While this solves your issue with the language, I'm not sure you'll be able to work around the repeated field, to get a pointer value. Maybe this thread might be helpful - https://stackoverflow.com/a/25637833/13183366

Matej G.
  • 11
  • 1
  • 2