* Paul J. Lucas <[email protected]> [180728 14:43]:
> First, please forgive the length of the example code. It's as small a
> distillation of my original code as I could manage that still
> illustrates the issue. The code is here:
>
> https://play.golang.org/p/MjyFize1iOW
>
> [trimmed]
>
> The problem is that root's activeChild 'C' is of type:
>
> *struct { main.CoreParent; S main.CoreState }
>
> When the code calls CoreParent.Enter() that calls CoreState.Enter(),
> the type of the receiver 's' is converted into:
>
> *main.CoreState
>
> Then 's' is passed as an argument to switchActiveChildTo() that gets
> assigned back to root's activeChild (which is 'C'). The type of
> activeChild changes even though it's the same object! It's as if its
> type was "sliced" down to its "base" type.
>
> When Exit() is eventually called on 'C', only CoreState.Exit() is
> called since it's type is now *main.CoreState rather than the original
> struct.
>
> Is such type "slicing" expected behavior? (This was a most annoying
> bug to track down.)
I haven't seen any replies to your question, so I will try to help you
understand what is happening. Indeed, your analysis that passing the
receiver of the CoreState.Enter method to parent.switchActiveChildTo
causes root.activeChild to change from one concrete type to another is
correct.
It appears that you are trying to use interfaces and embedding to
achieve inheritance and virtual methods, but Go does not have
inheritance or virtual methods. Calling a method of an embedded field
on the containing struct is the same as calling the method on the
embedded field; the receiver is that of the embedded field not of the
containing struct. E.g.
type E struct { }
func (e E) Foo() { }
type T struct {
E
a int
}
var t T
Calling t.Foo() is identical to calling t.E.Foo(), and either way, Foo
has no intrinsic ability to access t.a.
Note that a Parent is a State with an additional method. If a Parent
has State independent of its children, implement ParentState and
LeafState, each with their own implementation of the State interface,
giving ParentState the additional method required by Parent.
On the other hand, if a Parent does not have State of its own, but
merely exposes the State of its active child, don't include CoreState
(from your code, or CommonState from my code below) as a field of the
concrete type implementing Parent, but implement the State interface as
covers that call their counterparts on the active child.
If there is a large set of common data that all concrete types
implementing State will have, you can use embedding for the data, but
not for methods that would, in a language like C++, be virtual. E.g.
type CommonState struct {
name string
parent Parent
}
func (c *CommonState) Name() string {
return c.name
}
func (c *CommonState) Parent() Parent {
return c.parent
}
type LeafState struct {
CommonState
}
func (l *LeafState) Enter(msg string) {
// implementation for LeafState
}
func (l *LeafState) Exit(msg string) {
// implementation for LeafState
}
type ParentState struct {
CommonState
children []State
activeChild State
}
func (p *ParentState) Enter(msg string) {
// implementation for ParentState
}
func (p *ParentState) Exit(msg string) {
// implementation for ParentState
}
func (p *ParentState) switchActiveChildTo() {
// implementation for ParentState
}
Because the Name and Parent methods of a type implementing State do not
need to have access to anything except fields in CommonState, these
methods can be safely defined on CommonState which can be embedded,
promoting these methods.
Note that CommonState does not implement all of the State interface, but
when embedded, its methods can be used by the containing struct as part
of the State interface.
One further comment: you have CoreMachine embedding Machine. This is
extraneous. CoreMachine implements Machine by virtue of having all the
necessary methods. Embedding Machine causes CoreMachine to reserve
memory for a field holding a Machine interface variable which is never
used.
...Marvin
--
You received this message because you are subscribed to the Google Groups
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
For more options, visit https://groups.google.com/d/optout.