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

protoc-gen-go: generate Setters for oneof fields #283

Open
thakkarparth007 opened this issue Jan 28, 2017 · 16 comments
Open

protoc-gen-go: generate Setters for oneof fields #283

thakkarparth007 opened this issue Jan 28, 2017 · 16 comments
Labels
generator-proto-option involves generators respecting a proto option to control generated source output proposal proposal-hold
Milestone

Comments

@thakkarparth007
Copy link

The library provides only Getters for oneof fields. Because of this, setting oneof fields is painful. And this gets especially painful when the oneof fields are themselves proto messages. For example:

message RequestWrapper {
    string request_id = 1;
    string session_id = 2;

    oneof request {
		actions.BuyStocksFromExchangeRequest buy_stocks_from_exchange_request = 3;
		actions.CancelAskOrderRequest cancel_ask_order_request = 4;
		actions.CancelBidOrderRequest cancel_bid_order_request = 5;
                . . .
    }
}

message ResponseWrapper {
    string request_id = 1;

    oneof response {
		actions.BuyStocksFromExchangeResponse buy_stocks_from_exchange_response = 3;
		actions.CancelAskOrderResponse cancel_ask_order_response = 4;
		actions.CancelBidOrderResponse cancel_bid_order_response = 5;
                . . .
    }
}

message DalalMessage {
    oneof message_type {
        RequestWrapper request_wrapper = 1;
        ResponseWrapper response_wrapper = 2;
        DataStreamUpdateWrapper data_stream_update_wrapper = 3;
    }
}

Here, The Response messages themselves have oneof fields. Creating a DalalMessage type, then, has to be done in this manner:

dm := &socketapi_proto.DalalMessage{}
dm.MessageType = &socketapi_proto.DalalMessage_ResponseWrapper{
	ResponseWrapper: &socketapi_proto.ResponseWrapper{
		Response: &socketapi_proto.ResponseWrapper_BuyStocksFromExchangeResponse{
			BuyStocksFromExchangeResponse: response.(*actions_proto.BuyStocksFromExchangeResponse),
		},
	},
}

This is clearly painful. Instead, if setter methods are provided, the same code would be much shorter and readable:

rw := &socketapi_proto.DalalMessage_ResponseWrapper{}
rw.SetResponse(response.(*actions_proto.BuyStocksFromExchangeResponse))

dm := &socketapi_proto.DalalMessage{}
dm.SetResponseWrapper(rw)

Of course, this can be shortened further if protobuf does not wrap the fields inside a oneof field, but I assume that will break a lot of people's code. I also don't know the side effects of not wrapping oneof fields.

Even if the oneof fields stay wrapped, I believe that providing Setters will neither break anyone's code, nor will cause any side effects.

Sample output after adding a Setter:

// Generated already
func (m *DalalMessage) GetRequestWrapper() *RequestWrapper {
	if x, ok := m.GetMessageType().(*DalalMessage_RequestWrapper); ok {
		return x.RequestWrapper
	}
	return nil
}

// Generated as per this proposal
func (m *DalalMessage) SetRequestWrapper(rw *RequestWrapper) {
	m.MessageType = &DalalMessage_RequestWrapper{
		RequestWrapper: rw,
	}
}

I'm sure there are others facing this issue. (#205).

@bcmills
Copy link
Contributor

bcmills commented Jan 30, 2017

Your example is already somewhat more verbose than necessary. It can be written today as:

dm := &socketapi_proto.DalalMessage{
  MessageType: &socketapi_proto.DalalMessage_ResponseWrapper{
    &socketapi_proto.ResponseWrapper{
      Response: &socketapi_proto.ResponseWrapper_BuyStocksFromExchangeResponse{
        response.(*actions_proto.BuyStocksFromExchangeResponse),
      },
    },
  },
}

It would be somewhat simpler if golang/go#12854 were accepted. Then your example could be written:

dm := &socketapi_proto.DalalMessage{
  MessageType: &socketapi_proto.DalalMessage_ResponseWrapper{{
    Response: &socketapi_proto.ResponseWrapper_BuyStocksFromExchangeResponse{
      response.(*actions_proto.BuyStocksFromExchangeResponse),
    },
  }},
}

@thakkarparth007
Copy link
Author

thakkarparth007 commented Feb 6, 2017

Hmm, I wasn't aware that we could omit the field name if there was only one field. Also, the golang/go#12854 PR would make it much simpler, but I still don't see how a setter could harm. It would provide a nice uniform interface - this way there'd be no need to access the oneof field, instead we could directly access the 'children' - so to say. So in the above, I could simply call dm.SetResponseWrapper() without of even knowing about existence of MessageType.

Also, setters are available in other languages.

yugui added a commit to grpc-ecosystem/grpc-gateway that referenced this issue Jun 17, 2017
The idea behind the change is similar to
golang/protobuf#283.
But it returns a non-nil pointer to an oneof option struct for
ease of use in both of unmarshaling and assignment.
yugui added a commit to grpc-ecosystem/grpc-gateway that referenced this issue Jun 17, 2017
The idea behind the change is similar to
golang/protobuf#283.
But it returns a non-nil pointer to an oneof option struct for
ease of use in both of unmarshaling and assignment.
@aajtodd
Copy link

aajtodd commented Aug 31, 2017

I too find this more painful than it needs to be.

Curious why is a wrapper type generated for oneof fields? As far as I can tell the wrapper types all just implement an interface isXYZ_Type.

@dsnet dsnet changed the title Proposal: Provide Setters for oneof fields proposal: protoc-gen-go: generate Setters for oneof fields Feb 14, 2018
@dsnet dsnet added the proposal label Feb 14, 2018
@danielbprice
Copy link

danielbprice commented Feb 22, 2018

Hmm, I wasn't aware that we could omit the field name if there was only one field. Also, the golang/go#12854 PR would make it much simpler, but I still don't see how a setter could harm.

@thakkarparth007 I think this is a good point, and it doesn't really look to me like golang/go#12854 will be accepted any time before Go2, if then. Whereas we could have a setter today.

I'm trying to clean up some sprawling, overloaded protobufs with too many fields by replacing with oneofs which more accurately describe each use case, but I am concerned that other developers in my project will reject the awkwardness required to wade through this nesting. It's really not ergonomic at all. For me, what gogo provides in its "onlyone" extension (https://github.com/gogo/protobuf/tree/master/plugin/union) is what I'm looking for, but I don't want to switch out to that if I can help it.

@awalterschulze
Copy link

onlyone is a solution I enjoy using, but unfortunately it also only works if your union is between different field types. So I have sympathy for fixing this in the general case.
gogoprotobuf really struggled coming up with a better solution for oneof
gogo/protobuf#168
The current generated code is far from something that I want to use (so I avoid using oneof), but coming up with a good solution in a language which does not support tagged unions is really hard.
My advice is to stop using Go ;)

@thakkarparth007
Copy link
Author

@aajtodd how the current implementation works is by generating a field for the one-of, rather than the children of one-of. For example:

message Foo {
    oneof bar {
        uint32 apples = 1;
        string oranges = 2;
    }
}

This creates a struct Foo with one field "bar". So if both "apples" and "oranges" - of types uint32, and string respectively are to be assigned to the same field "bar", then they must have the same type. That is achieved by wrapping them into a empty interface of a common name. That allows you to set bar to an integer, or to a string, or whatever you have inside the "oneof".

abronan pushed a commit to abronan/grpc-gateway that referenced this issue Mar 7, 2018
The idea behind the change is similar to
golang/protobuf#283.
But it returns a non-nil pointer to an oneof option struct for
ease of use in both of unmarshaling and assignment.
@danielbprice
Copy link

Your example is already somewhat more verbose than necessary. It can be written today as:

@bcmills @thakkarparth007 FWiW I finally got around to trying this, and go vet flags it:

.../blah.go:100: bg/common_msg.Blah_Blah composite literal uses unkeyed fields

So for us it's a non-starter, as the rest of our codebase is vet clean.

@davidraleigh
Copy link

I believe a dedicated setter would be a great improvement.

A setter would make the messages less verbose AND it would allow proto file providers to document an API usage pattern that won't break if a oneof block is removed or added in the future.

For Java, C++, JavaScript and Python the way you set parameters for messages doesn't change if the proto file uses oneof blocks or doesn't use them. For Go, since there is no dedicated uniform setter method style, a users code will break if there is a removal or an insertion of a oneof definition. You're essentially locked into that definition with Go, but with other protobuf languages you can change it over time.

I would like to use oneof and I'm willing to commit to not taking them out in the future, but it's so absurdly verbose, I think users will be put off from using the client library all together. A setter method would be a great way for the message building process to still be manageable to someone learning a new library.

Below is an example of with and without oneof.

        operator := &pb.OperatorRequest{
		LeftGeoms:&pb.OperatorRequest_LeftGeometryBag{&serviceGeometry}, 
		RightGeoms:&pb.OperatorRequest_RightGeometryBag{&cutterGeometry}}

	operator := &pb.OperatorRequest{
		LeftGeometryBag:&serviceGeometry, 
		RightGeometryBag:&cutterGeometry}

I'm new to Go, so I maybe misunderstanding how to use protoc generated code.

@dsnet
Copy link
Member

dsnet commented May 10, 2018

The current solution is certainly not ideal, but I'm going to put this on hold for until there's a resolution on golang/go#19412.

@danielbprice
Copy link

@dsnet, I recently read most (but not all) of golang/go#19412, and it seems:

  • There's not any sort of concrete plan to go forward with that proposal.
  • It would exclusively target Golang 2.

Golang 1.x seems likely to be around for a long time; a solution now would benefit many implementors at least for several years to come. Could this be designed in such a way that it's not obviously clashing with the aforementioned proposal?

nvanbenschoten added a commit to nvanbenschoten/cockroach that referenced this issue Jul 3, 2018
…chResponse

All Requests and Responses pass through RequestUnion/ResponseUnion structs
when they are added to BatchRequests/BatchResponses. In order to ensure
that only one Request type can be assigned to one of these RequestUnion
or ResponseUnion structs, we currently use gogoproto's approach to tagged
unions: the `gogoproto.onlyone` option.

This option was introduced before proto3. Proto3
then added the `oneof` option, which for all intents and purposes addresses
the same issue: https://developers.google.com/protocol-buffers/docs/proto#oneof.
However, there is one major difference between the two options, which
is in their generated code. `gogoproto.onlyone` will generate
a single flat struct with pointers to each possible variant type.
`oneof` will generate a union interface and an interface "wrapper"
struct for each variant type. The effect of this is that `onlyone`
will generate code that looks like this:

```
type Union struct {
    Variant1 *Variant1Type
    Variant2 *Variant2Type
    ...
}
```

While `oneof` will generate code the looks like this:

```
type Union struct {
    Value isUnion_Value
}

type isUnion_Value interface {
    ...
}

type Union_Variant1 struct {
    Variant1 *Variant1Type
}

type Union_Variant2 struct {
    Variant2 *Variant2Type
}
```

There are pretty obvious tradeoffs to each. For one, `oneof` introduces an
extra layer of indirection, which forces an extra allocation. It also doesn't
generate particularly useful setters and getters. On the other hand, `onlyone`
creates a large struct that grows linearly with the number of variants.
Neither approach is ideal, and there has been **A LOT** of discussion on this:
- golang/protobuf#78
- golang/protobuf#283
- gogo/protobuf#103
- gogo/protobuf#168

Clearly neither approach is ideal, ergonomically or with regard to performance.
However, over time, the tradeoff has been getting worse for us and its time we
consider switching over to `oneof` in `RequestUnion` and `ResponseUnion`. These
structs have gotten huge as more and more request variants have been added:
`RequestUnion` has grown to 328 bytes and `ResponseUnion` has grown to 320 bytes.
It has gotten to the point where the wasted space is non-negligible.

This change switches over to `oneof` to shrink these union structs down to more
manageable sizes (16 bytes). The downside of this is that in reducing the struct
size we end up introducing an extra allocation. This isn't great, but we can avoid
the extra allocation in some places (like `BatchRequest.CreateReply`) by grouping
the allocation with that of the Request/Response itself. We've seen previous cases
like cockroachdb#4216 where adding in an extra allocation/indirection is a net-win if it
reduces a commonly used struct's size significantly.

The other downside to this change is that the ergonomics of `oneof` aren't quite
as nice as `gogo.onlyone`. Specifically, `gogo.onlyone` generates getters and
setters called `GetValue` and `SetValue` that provide access to the wrapped
`interface{}`, which we can assert to a `Request`. `oneof` doesn't provide
such facilities. This was the cause of a lot of the discussions linked above.
While this isn't ideal, I think we've waited long enough (~3 years) for a
resolution on those discussions. For now, we'll just generate the getters
and setters ourselves.

This change demonstrated about a 5% improvement when running kv95 on my local
laptop. When run on a three-node GCE cluster (4 vCPUs), the improvements were
less pronounced but still present. kv95 showed a throughput improvement of 2.4%.
Running kv100 showed an even more dramatic improvement of 18% on the GCE cluster.
I think this is because kv100 is essentially a hot loop where all reads miss
because the cluster remains empty, so it's the best case for this change. Still,
the impact was shocking.

Release note (performance improvement): Reduce the memory size of commonly used
Request and Response objects.
nvanbenschoten added a commit to nvanbenschoten/cockroach that referenced this issue Jul 3, 2018
…chResponse

All Requests and Responses pass through RequestUnion/ResponseUnion structs
when they are added to BatchRequests/BatchResponses. In order to ensure
that only one Request type can be assigned to one of these RequestUnion
or ResponseUnion structs, we currently use gogoproto's approach to tagged
unions: the `gogoproto.onlyone` option.

This option was introduced before proto3. Proto3
then added the `oneof` option, which for all intents and purposes addresses
the same issue: https://developers.google.com/protocol-buffers/docs/proto#oneof.
However, there is one major difference between the two options, which
is in their generated code. `gogoproto.onlyone` will generate
a single flat struct with pointers to each possible variant type.
`oneof` will generate a union interface and an interface "wrapper"
struct for each variant type. The effect of this is that `onlyone`
will generate code that looks like this:

```
type Union struct {
    Variant1 *Variant1Type
    Variant2 *Variant2Type
    ...
}
```

While `oneof` will generate code the looks like this:

```
type Union struct {
    Value isUnion_Value
}

type isUnion_Value interface {
    ...
}

type Union_Variant1 struct {
    Variant1 *Variant1Type
}

type Union_Variant2 struct {
    Variant2 *Variant2Type
}
```

There are pretty obvious tradeoffs to each. For one, `oneof` introduces an
extra layer of indirection, which forces an extra allocation. It also doesn't
generate particularly useful setters and getters. On the other hand, `onlyone`
creates a large struct that grows linearly with the number of variants.
Neither approach is ideal, and there has been **A LOT** of discussion on this:
- golang/protobuf#78
- golang/protobuf#283
- gogo/protobuf#103
- gogo/protobuf#168

Clearly neither approach is ideal, ergonomically or with regard to performance.
However, over time, the tradeoff has been getting worse for us and its time we
consider switching over to `oneof` in `RequestUnion` and `ResponseUnion`. These
structs have gotten huge as more and more request variants have been added:
`RequestUnion` has grown to 328 bytes and `ResponseUnion` has grown to 320 bytes.
It has gotten to the point where the wasted space is non-negligible.

This change switches over to `oneof` to shrink these union structs down to more
manageable sizes (16 bytes). The downside of this is that in reducing the struct
size we end up introducing an extra allocation. This isn't great, but we can avoid
the extra allocation in some places (like `BatchRequest.CreateReply`) by grouping
the allocation with that of the Request/Response itself. We've seen previous cases
like cockroachdb#4216 where adding in an extra allocation/indirection is a net-win if it
reduces a commonly used struct's size significantly.

The other downside to this change is that the ergonomics of `oneof` aren't quite
as nice as `gogo.onlyone`. Specifically, `gogo.onlyone` generates getters and
setters called `GetValue` and `SetValue` that provide access to the wrapped
`interface{}`, which we can assert to a `Request`. `oneof` doesn't provide
such facilities. This was the cause of a lot of the discussions linked above.
While this isn't ideal, I think we've waited long enough (~3 years) for a
resolution on those discussions. For now, we'll just generate the getters
and setters ourselves.

This change demonstrated about a 5% improvement when running kv95 on my local
laptop. When run on a three-node GCE cluster (4 vCPUs), the improvements were
less pronounced but still present. kv95 showed a throughput improvement of 2.4%.
Running kv100 showed an even more dramatic improvement of 18% on the GCE cluster.
I think this is because kv100 is essentially a hot loop where all reads miss
because the cluster remains empty, so it's the best case for this change. Still,
the impact was shocking.

Release note (performance improvement): Reduce the memory size of commonly used
Request and Response objects.
craig bot pushed a commit to cockroachdb/cockroach that referenced this issue Jul 3, 2018
27112: roachpb: replace `gogoproto.onlyone` with `oneof` in BatchRequest/BatchResponse r=nvanbenschoten a=nvanbenschoten

All Requests and Responses pass through RequestUnion/ResponseUnion structs
when they are added to BatchRequests/BatchResponses. In order to ensure
that only one Request type can be assigned to one of these RequestUnion
or ResponseUnion structs, we currently use gogoproto's approach to tagged
unions: the `gogoproto.onlyone` option.

This option was introduced before proto3. Proto3
then added the `oneof` option, which for all intents and purposes addresses
the same issue: https://developers.google.com/protocol-buffers/docs/proto#oneof.
However, there is one major difference between the two options, which
is in their generated code. `gogoproto.onlyone` will generate
a single flat struct with pointers to each possible variant type.
`oneof` will generate a union interface and an interface "wrapper"
struct for each variant type. The effect of this is that `onlyone`
will generate code that looks like this:

```
type Union struct {
    Variant1 *Variant1Type
    Variant2 *Variant2Type
    ...
}
```

While `oneof` will generate code the looks like this:

```
type Union struct {
    Value isUnion_Value
}

type isUnion_Value interface {
    ...
}

type Union_Variant1 struct {
    Variant1 *Variant1Type
}

type Union_Variant2 struct {
    Variant2 *Variant2Type
}
```

There are pretty obvious tradeoffs to each. For one, `oneof` introduces an
extra layer of indirection, which forces an extra allocation. It also doesn't
generate particularly useful setters and getters. On the other hand, `onlyone`
creates a large struct that grows linearly with the number of variants.
Neither approach is great, and there has been **A LOT** of discussion on this:
- golang/protobuf#78
- golang/protobuf#283
- gogo/protobuf#103
- gogo/protobuf#168

Clearly neither approach is ideal, ergonomically or with regard to performance.
However, over time, the tradeoff has been getting worse for us and it's time we
consider switching over to `oneof` in `RequestUnion` and `ResponseUnion`. These
structs have gotten huge as more and more request variants have been added:
`RequestUnion` has grown to **328 bytes** and `ResponseUnion` has grown to **320 bytes**.
It has gotten to the point where the wasted space is non-negligible.

This change switches over to `oneof` to shrink these union structs down to more
manageable sizes (16 bytes each). The downside of this is that in reducing the struct
size we end up introducing an extra allocation. This isn't great, but we can avoid
the extra allocation in some places (like `BatchRequest.CreateReply`) by grouping
the allocation with that of the Request/Response itself. We've seen previous cases
like #4216 where adding in an extra allocation/indirection is a net-win if it
reduces a commonly used struct's size significantly.

The other downside to this change is that the ergonomics of `oneof` aren't quite
as nice as `gogo.onlyone`. Specifically, `gogo.onlyone` generates getters and
setters called `GetValue` and `SetValue` that provide access to the wrapped
`interface{}`, which we can assert to a `Request`. `oneof` doesn't provide
such facilities. This was the cause of a lot of the discussions linked above.
While it we be nice for this to be resolved upstream, I think we've waited long
enough (~3 years) for a resolution to those discussions. For now, we'll just
generate the getters and setters ourselves.

This change demonstrated about a **5%** improvement when running kv95 on my local
laptop. When run on a three-node GCE cluster (4 vCPUs), the improvements were
less pronounced but still present. kv95 showed a throughput improvement of **2.4%**.
Running kv100 showed a much more dramatic improvement of **18%** on the three-node
GCE cluster. I think this is because kv100 is essentially a hot loop where all reads miss
because the cluster remains empty, so it's the best-case scenario for this change. Still,
the impact was shocking.

Release note (performance improvement): Reduce the memory size of commonly used
Request and Response objects.

27114: opt/sql: fix explain analyze missing option r=asubiotto a=asubiotto

ConstructExplain previously ignored the ANALYZE option so any EXPLAIN
ANALYZE statement would result in execution as an EXPLAIN (DISTSQL)
statement. The ANALYZE option is now observed in ConstructExplain.

Additionally, the stmtType field from the explainDistSQLNode has been
removed because it was not necessary and it was unclear how to pass this
from the `execFactory`.

Release note: None

27116: Makefile: learn that roachtest depends on optimizer-generated code r=benesch a=benesch

As mentioned in cd4415c, the Makefile will one day be smart enough to
deduce this on its own, but for now it's simpler to explicitly list the
commands that require generated code. Note that the simple but coarse
solution of assuming that all commands depend on generated code is
inviable as some of these commands are used to generate the code in the
first place.

Release note: None

27119: storage: extract replica unlinking into store method r=tschottdorf a=benesch

Extract some code that was duplicated in three places into a dedicated
helper method. Prerequisite for #27061.

Release note: None

Co-authored-by: Nathan VanBenschoten <nvanbenschoten@gmail.com>
Co-authored-by: Alfonso Subiotto Marqués <alfonso@cockroachlabs.com>
Co-authored-by: Nikhil Benesch <nikhil.benesch@gmail.com>
Tommy-42 pushed a commit to Tommy-42/grpc-gateway that referenced this issue Sep 26, 2018
The idea behind the change is similar to
golang/protobuf#283.
But it returns a non-nil pointer to an oneof option struct for
ease of use in both of unmarshaling and assignment.
Tommy-42 pushed a commit to Tommy-42/grpc-gateway that referenced this issue Sep 26, 2018
The idea behind the change is similar to
golang/protobuf#283.
But it returns a non-nil pointer to an oneof option struct for
ease of use in both of unmarshaling and assignment.
@kv1122
Copy link

kv1122 commented Oct 21, 2018

So after a good hour or so of copy/pasting, I now have setters on my messages.

Hay at least in Go, you can add methods to a type in another file:)

extra.go:

func (r *Req) SetXXXReq(req *XXXReq) {
	r.Body = &Req_XXXReq{XXXReq: req}
}

func (r *Rsp) SetYYYRsp(rsp *YYYRsp) {
	r.Body = &Rsp_YYYRsp{YYYRsp: rsp}
}

@dsnet dsnet added the generator-proto-option involves generators respecting a proto option to control generated source output label Aug 7, 2019
@dsnet
Copy link
Member

dsnet commented Aug 7, 2019

By default we cannot enable this feature since the "SetXXX" method could conflict with an actual field named "SetXXX". This is not a hypothetical scenario, but a real problem.

At best, this would be a generator option that one can opt-into.

@dsnet dsnet changed the title proposal: protoc-gen-go: generate Setters for oneof fields protoc-gen-go: generate Setters for oneof fields Mar 4, 2020
@bogdandrutu
Copy link

Don't know if this was already answered, but I saw this question asked couple of times, why is it necessary to have a wrapper around other messages in an oneof? I do understand why you need the message for the primitives but not sure why for messages. Because of that an unnecessary extra allocation is needed.

I know may be too late to change but want to understand if I miss something

@dsnet dsnet added this to the unplanned milestone Mar 29, 2021
@gonzojive
Copy link

By default we cannot enable this feature since the "SetXXX" method could conflict with an actual field named "SetXXX". This is not a hypothetical scenario, but a real problem.

At best, this would be a generator option that one can opt-into.

It seems you could work around method name conflicts the same way other name conflicts are handled - append an underscore or something.

I have no problem with an option but I disagree that setters would need to be an opt-in at best.

@reddaly
Copy link

reddaly commented Nov 9, 2022

It sounds like how to handle the conflict in a case like the following is the main complication of the proposal:

message Collection {
  Collection set_of_numbers = 1;
  oneof contrived_oneof {
    Bar of_numbers = 2;
  }
}
type Thing struct {
  SetOfNumbers *Collection // should this be called SetOfNumbers_, or should the setter be called SetOfNumbers_(?
  ContrivedOneof Thing_ContrivedOneof
}
// ...
func (m *Thing) SetOfNumbers_(v *Bar) { /* ... */ }

/// OR

type Thing struct {
  SetOfNumbers_ *Collection
  ContrivedOneof Thing_ContrivedOneof
}
// ...
func (m *Thing) SetOfNumbers(v *Bar) { /* ... */ }

There is probably logic in the reflection package that is relevant to handling the edge case, perhaps among other places.

@lcmaguire
Copy link

https://github.com/lcmaguire/protoc-gen-go-setters

there is a plugin if you want auto generated setters

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
generator-proto-option involves generators respecting a proto option to control generated source output proposal proposal-hold
Projects
None yet
Development

No branches or pull requests