xsigndll
xsigndll

Reputation: 553

Get outer/parent struct name

I'm facing a Golang begginers problem, and I don't know how to solve it correctly. Could you please help me?

Info: Even though this is against the concept of Go (not trying to be an OOP language) I'd like to discuss some solutions still.

I'd like to know the outer/parent struct name within the receiver/child. Please have a look at the following code (playground: https://play.golang.org/p/h6dARJQwidS )

package main

import (
    "fmt"
    "reflect"
)

type Parent struct {
    Id uint32
}

func (p *Parent) GetStructName() string {
    return reflect.TypeOf(p).Elem().Name()
}


type Child struct {
    Parent
}


func main() {
    myChild := Child{}
    fmt.Println(myChild.GetStructName()) // Gives "Parent" instead of "Child". How to get "Child"?
}

It displays "Parent", although the struct is a "Child". Can anyone tell me how to get the correct struct name? I've seen one 'solution' in another stackoverflow topic that works 'correctly' (Go - get parent struct), but I don't think this is a good solution.

Upvotes: 3

Views: 4050

Answers (3)

xsigndll
xsigndll

Reputation: 553

For completeness I wanted to share my solution to it (Playground: https://play.golang.org/p/tUhlz_o8Z7V).

As described in my initial question, the idea is from Go - get parent struct .

It's also related to a Go2 request that I've seen here: https://github.com/golang/go/issues/28254

package main

import (
    "fmt"
    "log"
    "reflect"
)

// we need an interface so methods are being embedded automatically
type IParent interface {
    Init(IParent)   IParent
}

// internal private fields, non-visible from the outside
type Parent struct {
    _IsInitialized  bool
    _Self           IParent
}

// init the struct, set "_Self" to it's caller
func (p *Parent) Init(o IParent) IParent {
    p._Self = o
    p._IsInitialized = true
    return o
}

// This method uses "_Self" to determine what it actually is
func (p *Parent) GetStructName() string {
    if !p._IsInitialized {
        log.Fatal("Struct not initialized. You may call 'myVar.Init(&myVar)' to initialize it.")
    }
    return reflect.TypeOf(p._Self).Elem().Name()
}

// Below childs have "Init()" from Parent, so they implement IParent automatically
// No need to duplicate any methods here anymore
type Child1 struct {
    Parent
}

type Child2 struct {
    Parent
}

type Child3 struct {
    Parent
}

type Child4 struct {
    Parent
}

func main() {
    myChild1 := Child1{}
    myChild1.Init(&myChild1) // Init object (set _Self on struct)
    fmt.Println(myChild1.GetStructName()) // Gives "Child1"

    myChild2 := Child2{}
    myChild2.Init(&myChild2) // Init object (set _Self on struct)
    fmt.Println(myChild2.GetStructName()) // Gives "Child2"

    myChild3 := Child3{}
    myChild3.Init(&myChild3) // Init object (set _Self on struct)
    fmt.Println(myChild3.GetStructName()) // Gives "Child3"

    myChild4 := Child4{}
    fmt.Println(myChild4.GetStructName()) // Fatal error
}

// Footnotes:
//---
//
//  This attempt tries to solve a go 'inheritance' problem although go is *NOT* meant to be an OOP language. It was a funny experiment still :-)
//  License: open domain, no attribution
//  https://www.xsigndll.com
//
//---

Upvotes: 4

balajeerc
balajeerc

Reputation: 4098

While Daniel's answer above answers the "why" of it, you can still "sorta" get the behaviour you are probably looking for via (a slightly ugly):

package main

import (
    "fmt"
    "reflect"
)

type NamedReturningType interface {
    GetStructName() string
}

type Parent struct {
    Id uint32
}

func (p *Parent) GetStructName() string {
    return reflect.TypeOf(p).Elem().Name()
}


type Child struct {
    Parent
}

func (c *Child) GetStructName() string {
    return reflect.TypeOf(c).Elem().Name()
}

func main() {

    myChild := Child{}
    fmt.Println(myChild.GetStructName())

    myParent := Parent{}
    fmt.Println(myParent.GetStructName())
}

(playground: https://play.golang.org/p/qEtoEulFSPy )

EDIT: Added an interface that these types can implement so as to make the code more generic.

Upvotes: -2

Daniel Conde Marin
Daniel Conde Marin

Reputation: 7742

GetStructName is a method of the type Parent not Child, also Golang does not have inheritance, instead there is struct embedding (also there is interface embedding), which is sort of like inheritance, but with a key difference:

When we embed a type, the methods of that type become methods of the outer type, but when they are invoked the receiver of the method is the inner type, not the outer one.

This basically means that when you call GetStructName, the receiver of the method is Parent (the inner or embedded type), and not Child.

This is fundamentally different from the typical class inheritance, and it explains the behaviour you're seeing.

It's well documented here.

Upvotes: 5

Related Questions