Looking at the following code, I would've
expected the top-level "x" selector of type
T4 to be ambiguous and hence invalid.
Thanks,
Anthony
package main
type T1 struct { x int }
type T2 struct { x int }
type T3 struct { T1 }
type T4 struct { T1; T3 }
type T5 struct { T1; T2 }
func main() {
_ = T1{}.x
_ = T2{}.x
_ = T3{}.x
_ = T4{}.x // why is this allowed?
_ = T5{}.x // ERROR "ambiguous DOT reference"
}
Indeed, you're right, and that's a bug. Would you mind to file an
issue about it?
--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/blog
http://niemeyer.net/twitter
I'm not sure it's a bug.
Look at test/fixedbugs/bug253.go
Anthony
>> What was the rationale for allowing selectors
>> with the same name but at different depths in
>> nested anonymous fields?
>>
>> Looking at the following code, I would've
>> expected the top-level "x" selector of type
>> T4 to be ambiguous and hence invalid.
>
> Indeed, you're right, and that's a bug. Would you mind to file an
> issue about it?
It's not a bug. Working as intended, as the text from Effective Go explains.
-rob
This doesn't address my question but
the paragraph above this one does. :)
Thanks everyone,
Anthony
What was the rationale for allowing selectors
with the same name but at different depths in
nested anonymous fields?
I can't see how it covers that case. It's obviously ambiguous.
What will this print:
type T1 struct { x int }
type T2 struct { x int }
type T3 struct { T1 }
type T4 struct { T1; T3 }
func main() {
t := T4{}
t.T1.x = 1
t.T3.x = 2
println(t.x)
By the spec, that will print "1".
> A selector f may denote a field or method f of a type T, or it may refer to a field or method f of a nested anonymous field of T. The number of anonymous fields traversed to reach f is called its depth in T. The depth of a field or method f declared in T is zero. The depth of a field or method f declared in an anonymous field A in T is the depth of f in A plus one.
> For a value x of type T or *T where T is not an interface type, x.f denotes the field or method at the shallowest depth in T where there is such an f. If there is not exactly one f with shallowest depth, the selector expression is illegal.
I see, it's a matter of depth. It feels somewhat error prone still,
but it's not ambiguous.
Yeah, the spec is clear, and Effective Go mentions it as well (above
the text quoted in this thread).
I can see why it's allowed. It'd be somewhat boring to have to pick
names based on what is embedded or not elsewhere. It still feels a
bit easier than one would like to have logic broken in strange ways,
though, since it is possible for a name to magically start referring
to something else unintendedly. That said, we haven't seen anyone
reporting that yet, so it feels like the pragmatic benefit is worth
the risk.
So that you can define
type T struct {
U
x int
}
and not worry about whether U contains an 'x int' too.
Russ
Or whether might acquire an 'x' in the future.
-rob
That's the tricky bit. Adding an x suddenly changes existing code to
a complete new meaning without notices. That's the only detail that
feels a bit unsettling, but the trade-off feels useful.
Would it not make sense then to simply disallow duplicate names but
allow for some sort of alias system to distinguish between them?
Doesn't seem worth the trouble at this point. The current system
works in practice, and my worries are mostly theoretical.
Would it not make sense then to simply disallow duplicate names but
allow for some sort of alias system to distinguish between them?
> It's not a bug. Working as intended, as the text from Effective Go explains.I can't see how it covers that case. It's obviously ambiguous.
What will this print:
I, on the other hand, should be more humble when pointing out to
people what's a bug or not, since clearly I'm still ignorant on parts
of the spec.
> A more important question is "Do people write Go code like this? And what do
> they expect of it?"
> There's nothing like that in the standard library. In fact, I've never seen
> a "multiple-inheritance" scenario like this in real Go code.
I was more concerned about people that don't want to write logic like
this, but do so unintendedly. You can hit such a situation with
single inheritance, with the simplest scenario being:
type T1 struct { x int }
type T2 struct { T1 }
Now, add x to T2, and you switch every t2.x call to mean something else.
Of course, this is precisely the case that the feature design was
intended to cover in the first place, and it is useful as such, so I
take this more as a "I should watch out when adding fields when
embedded types exist" than "the feature is broken".