Live data from Hacker News

Be careful with Go struct embedding

mattjhall.co.uk

91–100 of 101 posts

Re: Be careful with Go struct embedding

#91
post #87
post #78

Earlier quoted context omitted.

I'm making an analogy using simple assignments to show more clearly that it's actually counter-intuitive, I'm not claiming what you seem to be refuting, which at best seems to be beside the point.

And the analogy is invalid, because it completely replaces the intuitive thing that's happening with an unintuitive thing that isn't happening. There are no "latter operations or (re)definitions" here.

> There are no "latter operations or (re)definitions" here.

Yes, in the literal narrow sense, there is no such thing in the submitted article (if it isn't already clear, I'm referring to my own example). That's why it's an analogy. I don't know the precise term that go uses for this, closest is probably "shadowing", but again it doesn't matter, it is besides the point. The point is that the exhibited behaviour is unintuitive, in contrast to what the others are saying.

> it completely replaces the intuitive thing that's happening with an unintuitive thing that isn't happening

What is the intuitive thing are you referring to here? If it's my example, then you are in total agreement with me, but you seem to think otherwise. If you are referring to the linked article, then you are just merely invoking tautology, to disagree with me. It's intuitive because you said so, therefore my analogy is invalid. Did I get that right?

Re: Be careful with Go struct embedding

#92
post #56

Over the course of ~10 years of writing Go, my ratio of "embedding a struct" to "regretting embedding a struct" is nearly 1:1. I do not embed structs anymore. It is almost always a mistake. I would confidently place it in the "you should be required to import 'unsafe' to use this feature" bin.

I think there are a handful of cases where it is a nice-to-have and would be sad if it was removed in a hypothetical Go 2. Making a utility wrapper struct that overrides a method or adds new helper methods while keeping the rest of the interface is the most common example, though there are also some JSON spec type examples which are a little more esoteric. However, you need to be mentally prepared to switch to the bo…

yup, less than 24 hours after writing that comment, I found myself embedding a struct so that I could override one method in a test, haha

Re: Be careful with Go struct embedding

#93
post #66

Earlier quoted context omitted.

The missing something is manually creating the structs, not casting.

You said struct embedding could be used for discriminated unions, but there's no mechanism to discriminate between union variants here.

Go simply doesn't have discriminated unions, so a number of pattern can all be called "discriminated unions" in Go. I was simply emphasizing that sharing common fields between pure data structs with struct embedding (commonly seen in but not limited to discriminated unions) but now people are weirdly hung up on discriminated unions. I just showed a data model with a discriminator (.Type), there are a number of mechanisms to discriminate depending on your actual needs. You can make the types conform to an interface, pass an interface value around and cast to specific types. You can get a fat row with a bunch of left joins from your database then immediate create type-specific structs and call type-specific methods with them. You can get a discriminated union on the wire, unmarshal the type field first, then choose the type-specific struct to unmarshal to. Etc. These are largely irrelevant in a discussion about type embedding.

Re: Be careful with Go struct embedding

#94
I don't understand how this got so many upvotes. Embedded struct fields are never "promoted", you always need to access them via the embedded type's name, so there's nothing to conflict.

The only thing "promoted" are the functions associated with the embedded types, and when those actually conflicts, the compiler will tell you, as expected.

Re: Be careful with Go struct embedding

#95
post #93

Earlier quoted context omitted.

You said struct embedding could be used for discriminated unions, but there's no mechanism to discriminate between union variants here.

Go simply doesn't have discriminated unions, so a number of pattern can all be called "discriminated unions" in Go. I was simply emphasizing that sharing common fields between pure data structs with struct embedding (commonly seen in but not limited to discriminated unions) but now people are weirdly hung up on discriminated unions. I just showed a data model with a discriminator (.Type), there are a number of mechan…

> so a number of pattern can all be called "discriminated unions"

Assuming they've got discriminators and some sense of type union, sure.

> I just showed a data model with a discriminator (.Type)

Which won't let you recover the additional fields from a pointer because you can't downcast, so that's insufficient for a union. AFAIK you need to combine this with interfaces, which I already know how to do.

> These are largely irrelevant in a discussion about type embedding.

Don't tell me, you brought it up.

Re: Be careful with Go struct embedding

#96

I don't understand how this got so many upvotes. Embedded struct fields are never "promoted", you always need to access them via the embedded type's name, so there's nothing to conflict. The only thing "promoted" are the functions associated with the embedded types, and when those actually conflicts, the compiler will tell you, as expected.

> you always need to access them via the embedded type's name, so there's nothing to conflict.

The article talks about "opts.URL" in its example being accepted by the compiler, which accesses "opts.FooService.URL" without using the embedded type's name.

Re: Be careful with Go struct embedding

#97
post #91
post #87

Earlier quoted context omitted.

And the analogy is invalid, because it completely replaces the intuitive thing that's happening with an unintuitive thing that isn't happening. There are no "latter operations or (re)definitions" here.

> There are no "latter operations or (re)definitions" here. Yes, in the literal narrow sense, there is no such thing in the submitted article (if it isn't already clear, I'm referring to my own example). That's why it's an analogy. I don't know the precise term that go uses for this, closest is probably "shadowing", but again it doesn't matter, it is besides the point. The point is that the exhibited behaviour is uni…

> Yes, in the literal narrow sense, there is no such thing in the submitted article

Therefore your analogy is invalid, because your example is doing something entirely different and throws away nested structs that the whole thing is about.

> The point is that the exhibited behaviour is unintuitive, in contrast to what the others are saying.

Why?

> Did I get that right?

No. Let's stick to the original example and add the order of operations from your example.

  type A struct {
    X string
  }

  type Nested struct {
    X string
  }

  type B struct {
    Nested
  }

  type Combined struct {
    A
    B
  }

  c := Combined{}
  c.X = "example.com"
  c.Nested.X = "something completely different"

  fmt.Println(c.X)
Do you still expect this to print "something completely different" or does this look intuitive now?

The unintuitive part is that this works in the first place and doesn't throw an error:

  type Combined struct {
    //A
    B
  }

  c := Combined{}

  //c.X = "example.com"
  c.Nested.X = "something completely different"
  fmt.Println(c.X)
But if you know about this unintuitive feature and are relying on it instead of accessing the fields by their fully qualified names, then you should already have a gnawing feeling that asks you "what happens when there are conflicts?" (and the answer is - it does the intuitive thing)

Re: Be careful with Go struct embedding

#98
post #93

Earlier quoted context omitted.

Go simply doesn't have discriminated unions, so a number of pattern can all be called "discriminated unions" in Go. I was simply emphasizing that sharing common fields between pure data structs with struct embedding (commonly seen in but not limited to discriminated unions) but now people are weirdly hung up on discriminated unions. I just showed a data model with a discriminator (.Type), there are a number of mechan…

> so a number of pattern can all be called "discriminated unions" Assuming they've got discriminators and some sense of type union, sure. > I just showed a data model with a discriminator (.Type) Which won't let you recover the additional fields from a pointer because you can't downcast, so that's insufficient for a union. AFAIK you need to combine this with interfaces, which I already know how to do. > These are lar…

It’s almost like I brought it up in passing because it’s a somewhat relevant concrete use case, rather than brought it up to have people who “already know how to do” to chastise me for not writing a full treatise on the use case.

Re: Be careful with Go struct embedding

#100
post #97
post #91

Earlier quoted context omitted.

> There are no "latter operations or (re)definitions" here. Yes, in the literal narrow sense, there is no such thing in the submitted article (if it isn't already clear, I'm referring to my own example). That's why it's an analogy. I don't know the precise term that go uses for this, closest is probably "shadowing", but again it doesn't matter, it is besides the point. The point is that the exhibited behaviour is uni…

> Yes, in the literal narrow sense, there is no such thing in the submitted article Therefore your analogy is invalid, because your example is doing something entirely different and throws away nested structs that the whole thing is about. > The point is that the exhibited behaviour is unintuitive, in contrast to what the others are saying. Why? > Did I get that right? No. Let's stick to the original example and add…

> The unintuitive part is that this works in the first place and doesn't throw an error

What the hell? So you do agree that it's unintuitive but the supporting points you keep giving are completely, utterly tangential. That's what I have been saying all this time, that it's unintuitive, what you are even disagreeing with me for? The analogy?

I repeat this once again, I made the analogy to simplify and make it clear because some responses seems to miss it. I've already addressed your points, but you keep giving back the same supposed rebuttal, different words but same meaning. Nothing about what you say invalidates the analogy.

> But if you know about this unintuitive feature and are relying on it instead of accessing the fields by their fully qualified names, then you should already have a gnawing feeling that asks you "what happens when there are conflicts?" (and the answer is - it does the intuitive thing)

If you are deeply aware of the quirks, intuition no longer applies. You rely on intuition when you are in an unfamiliar situation. So again, nothing what you said just now supports any of your argument, whatever it is.

Post reply on HN