Am I the only one who found the described behavior to be intuitively correct? I did expect it to print "abc.com".

This may be intuitively correct, but to my mind it is architecturally wrong. A good language should not tolerate ambiguity and offer to guess which behavior is correct.

It's not ambiguous though. The behaviour is very clearly defined in the language spec.

https://go.dev/ref/spec#Selectors

As far as language specs go, Go's is really quite concise and I strongly encourage everyone I onboard to spend an afternoon reading it end to end at some point in their first couple weeks.

Why is it ambiguous though? The second URL is nested

Are thy not accessed like

opts.URL == abc.com

and

opts.BarConnectionOptions.URL == xyz.com

what leads you think otherwise?

If there were no duplicate URL field, say they were called FooURL and BarcoURL, you could access them as `opts.FooURL` and `opts.BarcoURL`. They are both fields of `opts`, via embedding. It's just that FooURL is embedded directly, while BarcoURL is coming from two levels of embedding.

I'm confused at the responses saying this intuitive. It's like saying:

  var x string
  x = "abc.com"
  x = "xyz.com"
  fmt.Println(x)
will print abc.com and that's totally expected.

The normal intuition would be that the latter operations or (re)definitions override the preceding ones.

It's not at all like saying that.

The order of operations has nothing to do with it.

  opts := Options{
    FooService: FooService{URL: "abc.com"},
    BarService: BarService{
      BarConnectionOptions: BarConnectionOptions{
        URL: "xyz.com",
      },
    },
  }
is equivalent to

  opts := Options{
    BarService: BarService{
      BarConnectionOptions: BarConnectionOptions{
        URL: "xyz.com",
      },
    },
    FooService: FooService{URL: "abc.com"},
  }

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?

> 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)

I don’t write Go at all but given the first example, also expected this.

I was very surprised that either example compiled, though.

Yeah it makes sense to me as well.

What is the intuition for that?

I think it’s something like:

“The general rule is that I may access the direct embeds of my type anonymously.”

Ok I don’t think anyone disagrees with that. But there are two embedded structs, both with a URL field.

But the level of nesting isn't the same; only one of them has a direct URL field.

You would get the same result if both URL fields were nested one level deeper. Being directly nested isn't the point.