Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

You start with the wrong premise that the messages shouldn't be copied in the first place.

Why?



Copying makes for surprising semantics, and prevents some representation changes.

An example w.r.t. the surprising semantics:

  var ms []mypb.Message = ppb.GetMessages() // A repeated submessage field
  for i, m := range ms {
    m.SetMyInt(i)
  }
  assert(ppb.GetMessages()[1].GetMyInt(1) == 1) // This would fail in general, due to SetMyInt acting on a copy.
This would not work as expected, as I highlighted in the comment. Basically, acting on value types means being very careful about identity. It makes it easy to make mistakes. I like data-driven code, but working around this (sometimes you'd want a copy, sometimes you wouldn't) would be a painful excercise.

You may have noticed that changing a heavily pointerized tree of types into value types often compiles with just a few changes, because Go automatically dereferences when needed. But it often won't work from a semantic point of view because the most intuitive way to modify such types uses copies (the range loop is a good example).

Now imagine changing the representation such that it carries a mutex, or another nocopy type. That would lead to issues unless those nocopy types would be encapsulated in a pointer. But then you get issues with initialization:

  var m mypb.Message // Value type, but what about the *sync.Mutex contained deep within?
Also consider laziness

  func process(lm mypb.LazyMessage) {
    if lm.GetSubMessage().GetInt() != 42 {
      panic("user is not enlightened")
    }
  }

  var lm mypb.LazyMessage
  process(lm) // Copy a fairly large struct.
  ln.GetSubMessage().GetInt() // Does lazy unmarshaling of sub_message redundantly.
  
If you want to make the argument that individual messages should be pointers, but slices should still be value slices. Then I have the following for you:

  ms := m.GetSubMessages() // []mypb.Message
  el := &ms[0]
  anotherEl := new(mypb.Message)
  ms.SetSubMessages(append(ms.GetSubMessages(), anotherEl)) // Can cause reallocation, now el no longer references to m.GetSubMessages()[0]. But it no reallocation happened, it does.
In practice, value typing leads to a bunch of issues.

Since you seem so sure of your position, I'm actually curious. How would you design the API, how would you use it? Do you have any examples I can look at of this style being used in practice?




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: