2

I've been playing with interfaces and structures in Golang in an attempt to implement "inheritance", but I am confident that I've done it in a wrong way. It would be easier to explain with an example.

I want to create structures of different living things. I want them to have the GetName() method:

type LivingThingProvider interface {
    GetName() string
}

Now, all of them should have names and birthdays. For that, I am creating a structure and embedding the interface to it:

type LivingThing struct {
    birthday string
    name     string
    LivingThingProvider
}

I want to add a couple of methods which would be the same for all living beings:

func (this *LivingThing) Initialize() {
    this.birthday = time.Now().Format("02.01.2006")
}

func (this LivingThing) GetBirthday() string {
    return this.birthday
}

Now, here are the structures that are supposed to "implement" LivingThing:

type Frog struct {
    insectsEaten int
    LivingThing
}

func (this Frog) GetName() string {
    return fmt.Sprintf("%s-the-Frog-proud-eater-of-%d-insects", this.name, this.insectsEaten)
}

type RobotMan struct {
    LivingThing
}

func (this RobotMan) GetName() string {
    h := sha256.New()
    h.Write([]byte(this.birthday))
    return fmt.Sprintf("%s-%X", this.name, h.Sum(nil))
}

In the main function, I am creating and adding a frog and a robotman to a slice, after that I loop over it:

func main() {
    fr := Frog{}
    fr.name = "Dizzy"
    fr.insectsEaten = 586
    fr.LivingThingProvider = fr

    rm := RobotMan{}
    rm.name = "Bender"
    rm.LivingThingProvider = rm

    fr.Initialize()
    rm.Initialize()

    entities := []LivingThing{fr.LivingThing, rm.LivingThing}

    for _, ent := range entities {
        fmt.Printf("Hi, I am %s!\n", ent.GetName())
        fmt.Printf("I was born on the %s.\n", ent.GetBirthday())
    }
}

Everything works as expected, but if I remove the GetName() method from the Frog or RobotMan structure, it will compile and panic after run:

panic: runtime error: invalid memory address or nil pointer dereference

Here's the playground link: https://play.golang.org/p/h2VgvdcXJQA

My questions are the following:

1. Is what I've done "dirty" in terms of Go? If so, how to do it in a correct way?

1a. Especially, is it fine to assign the structure itself to its embedded interface field (fr.LivingThingProvider = fr)?

2. Why doesn't Go compiler check if Frog and RobotMan implement the LivingThingProvider interface?

Thank you very much in advance!

5
  • 1. Yes, attempting to "implement" inheritance is the incorrect way to write Go code. The correct approach is to use composition. Commented Aug 25, 2018 at 22:38
  • 2. Both the types still implement the interface because they embed LivingThing which embeds LivingThingProvider which is unset and therefore nil and so you get the panic. If a struct type embeds another type, the methods of the embedded type become directly accessible through the embedding type which is why the two types still implement the interface even after you removed their methods. Commented Aug 25, 2018 at 22:46
  • 1
    ... also note that, interfaces are satisfied implicitly. You do not need to embed the interface to have the embedding type implement it. You implement an interface by declaring methods on the desired implementer with the same signature as the ones declared on the interface. Commented Aug 25, 2018 at 22:49
  • ... fixed: play.golang.org/p/dZCzoUo0gRg Commented Aug 25, 2018 at 22:54
  • @mkopriva, thank you very much! It appears to be so simple and clear after your explanation. Thank you again. Commented Aug 26, 2018 at 0:28

1 Answer 1

2

@mkopriva answer it and should get the credit but it looks like some explanation needed for classical inheritance programmers.

Interface define behavior like Name and Birthday, (In Go its idiomatic to omit the Get). Structs implement these interface behavior. Not embedding them.

Implementing an interface is all you need to do in order to "be" this interface. AKA duck type.

When struct T implement String() string T become a Stringer.

In this particular example we can see why In Golang a one method interface and interface embedding is so common.

Since RobotMan do not have a birthday really it whould be better in my opinion to create three interfaces Namer: Name() string, Burner:Birthday() time.Time and

type LivingThingProvider interface {
    Namer
    Burner
}

Struct composition is not a replacement for inheritance its a different way to reuse code.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.