0

I'm having some issues understanding Go's struct inheritance. I'm trying to do somewhat of an abstraction for an object type. See the example code below:

package main

type Animal struct{}
type Dog struct {
    Animal
    Color string
}

type Person struct {
    Name string
    Age  int
    Pet  *Animal
}

func main() {
    dog := &Dog{Color: "brown"}
    tom := &Person{Name: "Tom", Age: 13, Pet: dog}
}

This is causing a compilation error:

cannot use dog (type *Dog) as type *Animal in field value

What is the correct way to go about doing an abstraction like this? Is it possible in Go?

End goal for the example would be to have different types of Pets - Dog, Cat, Hamser, etc. Then be able to store that into a struct expecting type Animal.

To visualize, something like:

type Person struct {
    Name string
    Age  int
    Pet  *Dog OR *Cat OR *Hamster
}

Go Playground Link

6
  • And then do what? please elaborate Commented Nov 13, 2017 at 11:23
  • Well the actual implementation of this is in a web backend. It's an attempt to have a generic JSON response back to the client with Results as type []*DataModel where that could be filled with different types of data types, depending on the request. Commented Nov 13, 2017 at 11:26
  • 3
    Go does not have struct inheritance, it does however support struct embedding, which are two very different things. A struct of type Foo that embeds a struct of type Bar cannot be used in places where Bar is expected. All Foo gets from embedding Bar is access to Bar's fields and methods. So Foo IS NOT Bar, it only "knows about" Bar, while Bar does not even know, or care, about Foo at all. Commented Nov 13, 2017 at 11:33
  • 1
    "What is the correct way to go about doing an abstraction like this?" No, it is not. Go has no notion of inheritance and you should not try to model your code in a inheritance-based style. "Is it possible in Go?" No. You would use interfaces instead of parent classes. Commented Nov 13, 2017 at 11:53
  • 1
    Just to give you a clue as to how go's embedding works: it's composition, not inheritance. You could try and write: tom := &Person{Name: "Tom", Age: 13, Pet: &dog.Animal}. your dog variable contains a full Animal, but it is not an animal. It's is own type that consists of its own fields (Colour), and an Animal, whatever that type might be Commented Nov 13, 2017 at 13:50

2 Answers 2

2

Two working samples:

1- Try this:

package main

import (
    "fmt"
)

type Animal interface {
    color() string
}
type Dog struct {
    Color string
}

func (d Dog) color() string {
    return d.Color
}

type Person struct {
    Name string
    Age  int
    Pet  Animal
}

func main() {
    dog := &Dog{Color: "brown"}
    tom := &Person{Name: "Tom", Age: 13, Pet: dog}
    fmt.Println(tom.Pet.color())
}

2- Try this:

package main

import (
    "fmt"
)

type Animal struct{}
type Dog struct {
    Animal
    Color string
}

type Person struct {
    Name string
    Age  int
    Pet  interface{}
}

func main() {
    dog := &Dog{Color: "brown"}
    tom := &Person{Name: "Tom", Age: 13, Pet: dog}
    fmt.Println(tom.Name)
}
Sign up to request clarification or add additional context in comments.

3 Comments

I was trying to avoid using an empty interface for the reason that you could pass anything into it. For example, the Person could end up with a Bicycle as his Pet.
So you may use(1) based on functionality of Object. Is this Good for your case?
Thank you, your solution helped me find the bug in my code. I was incorrectly setting Pet to *Animal which is a pointer to the interface rather than having it be of the interface type itself. Thanks!
0

Just to add to the accepted answer, the use of interfaces or embedding is not really a requirement, at least not in this simple example.

For example, if you have a set of types that have the same structure but do not have any methods, i.e. pure data without behaviour, in such a case you can use a single type to represent the set and an additional "enum" to represent the "kind".

For example:

type AnimalKind string

const (
    AnimalKindDog AnimalKind = "dog"
    AnimalKindCat AnimalKind = "cat"
)

type Animal struct {
    Kind  AnimalKind
    Color string
}

// ...

dog := &Animal{Kind: AnimalKindDog, Color: "brown"}
tom := &Person{Name: "Tom", Age: 13, Pet: dog}

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.