(As a followup this question: nested struct initialization literals).
Now that I can initialize a struct with literals that are easy to write, I later in my code need access members of the parent struct, but without knowing the concrete derived type. It's like this:
type A struct {
MemberA string
}
type B struct {
A
MemberB string
}
And then I use it like this:
b := B {
A: A { MemberA: "test1" },
MemberB: "test2",
}
fmt.Printf("%+v
", b)
var i interface{} = b
// later in the code, I only know that I have something that has a nested A,
// I don't know about B (because A is in a library and B is in the
// calling code that uses the library).
// so I want to say "give me the A out of i", so I try a type assertion
if a, ok := i.(A); ok {
fmt.Printf("Yup, A is A: %+v
", a)
} else {
fmt.Printf("Aristotle (and John Galt) be damned! A is NOT A
")
}
// no go
The options I see are:
I could use reflection to look for a member called "A" and, assuming it's the right type, use it. This would be workable but less efficient and certainly more "clunky".
I could require the caller to implement an interface (like HasA { Aval() A }
or similar which returns an instance of A. So far this is the best idea I could think of.
The other point is that I could just have the caller pass a A value (i.e. in the example above, var i interface{} = b
becomes var i A = b.A
). But what's happening is I actually dynamically iterate over the members of B and do stuff with them, so I need that more "derived" type in order to that. (I've omitted that from the question because it's more just background as to why I'm running into this and is not pertinent to the technical answer of the question.)
It would be great if I could just "cast it to A", as you would in Java. Is there a more elegant way to do that.
It would be great if I could just "cast it to A", as you would in Java. Is there a more elegant way to do that.
Blind casting is almost always bad news for your code quality so it is actually quite good to make it difficult to do so.
I would go the interface route as it would also eliminate the interface{}
you use to store b
. If you indeed have a high variety of types and they only share that they embed A
an interface which offers AVal() A
seems good to me.
As a bonus, you could define AVal() A
on A
directly so you don't need to implement it for every type that embeds A
.
Example (on play):
type A struct {
MemberA string
}
func (a *A) AVal() *A {
return a
}
type B struct {
*A
MemberB string
}
type AEmbedder interface {
AVal() *A
}
func main() {
b := B {
A: &A { MemberA: "test1" },
MemberB: "test2",
}
var i AEmbedder = b
a := i.AVal()
fmt.Printf("Yup, A is A: %+v
", a)
}
Note that I'm now using a pointer value so that AVal() *A
does not return a copy but the respective instance of A
.
This worked for me. Although it's not directly casting like you wanted, it does give the correct object instance
fmt.Printf("It's", i.(B).A)
I hope it helps!
If you have an unknown type b, the only way to dig out an embedded field is through reflection.
It's not that clunky:
// obviously missing various error checks
t := reflect.ValueOf(i)
fmt.Printf("%+v
", t.FieldByName("A").Interface().(A))
Struct embedding is not inheritance, and trying to use it as such is going to continue to bring up issues like this. The way to achieve general polymorphism in go is to use interfaces.
I think the cleanest way to handle this situation is to use a common interface, with appropriate accessor methods for the fields you want to handle. You'll see examples of this the stdlib, e.g. http.ResponseWriter
, which has a Header()
method used to access the actual response headers.
Playing around with it for a bit, I got this to work: http://play.golang.org/p/-bbHZr-0xx
I'm not 100% sure on why this works, but my best theory is that when you call
a := i.(A)
you are trying to do an interface version of a typecast of whatever is stored in i
to A
. So you first need to tell it that it is actually B
, then you can access the A that is nested in it
a := i.(B).A