0

I have a some structs,

type Fruit struct {
  Name string
  Sweetness int
}
type Meat struct {
  Name string
  Bloodiness int 
}

Somtimes a person may eat some fruit, sometimes some meat. So we have another struct.

type Person struct {
  Name string
  Mealtype interface{} 
}

It's this 'Mealtype' interface{} bit I kind of made up to fix my issue. Go is allowing me to set either a Mealtype to be a Meat or Fruit struct, however. I can't seem to access any of the internal data from the struct. The fmt.Println( someperson.Mealtype ) doesn't offer my to access either .Bloodiness or .Sweetness

For example, if i do:

f := Fruit{}
f.Name = "Orange"
f.Sweetness = 10

p := Person{}
p.Name = "John"
p.Mealtype = f 

fmt.Println(p.Mealtype.Name) 

I get the error:

p.Mealtype.Name undefined (type interface{} has no field or method Name)
1
  • 2
    In Go there is no way to express what you want in a generic way. The closest you can get is the declare a getter method on the interface and have two types implement it. It's possible that, in future versions of Go, you will be able to do this using type constraints, but as of Go1.18 this is simply not possible. Commented May 19, 2022 at 16:17

4 Answers 4

1

If you want both Fruit and Meat to share a Name value that can be accessed, you probably want to create an interface that the two of them implement. For example

type Food interface {
    Name() string
}

func (f Fruit) Name() string {
    return f.name
}

func (m Meat) Name() string {
    return m.name
}

type Person struct {
  Name string
  Mealtype Food 
}

(note that the Meat and Fruit's field name is now lowercase, to avoid conflict with Name)

Then you should be able to call fmt.Println(p.Mealtype.Name()).

For completeness, you can also use type assertion, but that's probably not what you want to do in your example. But it would look something like this:

if fruit, ok := p.MealType.(Fruit); ok {
    fmt.Println(fruit.Name)
}
Sign up to request clarification or add additional context in comments.

Comments

0

You should describe Mealtype interface before using it. Like

type MealtypeInterface interface {
    Name() string
}

Then in type Person you define Mealtype as MealtypeInterface, not interface{}

type Person struct {
  Name string
  Mealtype MealtypeInterface 
}

Comments

0

an interface defines a consistent way of interacting with variables of inconsistent types. So you have to find a way to make accessing Bloodiness of meats and Sweetness of fruits consistently.

Here's one way that Sweetness and Bloodiness could be behind a consistent interface:

interface Edible {
   PrimaryAttribute() (string, int)
}

func (f Fruit)PrimaryAttribute() (string, int) {
  return "Sweetness", f.Sweetness
}

// and return Bloodiness, m.Bloodiness for meats of course

Its common for programmers used to interpreted languages like python or javascript, etc, to think of struct field names as runtime-accessible data. But this is only the case in Go with reflect, which I try to discourage new Go programmers to use. Ask yourself if you wouldn't rather define a consistent method of describing arbitrary named attributes of your edibles:

type Edible struct {
   FlavorAttributes map[string]int
}

fish_meat := &Edible{FlavorAttributes: map[string]int{"Bloodiness": 3}

Now Bloodiness and Sweetness are not fields, they are strings in a map, and more accessible at runtime.

It's common for programming books to talk about polymorphism in terms of human hierarchical organization (class Animal, class Dog, class Labrador hierarchy). Read "Design Patterns: Elements of Reusable Object-Oriented Software" for some good thoughts about why a lot of software doesn't end up implementing class hierarchies from the human perspective.

Comments

0

If you want generics that are on go 1.18, this is it

package main

import "fmt"

type Fruit struct {
    Name      string
    Sweetness int
}

type Meat struct {
    Name       string
    Bloodiness int
}

type Mealtype interface {
    Fruit | Meat
}

type Person[M Mealtype] struct {
    Name string
    Meal M
}

func main() {
    f := Fruit{}
    f.Name = "Orange"
    f.Sweetness = 10

    p := Person[Fruit]{}
    p.Name = "John"
    p.Meal = f

    fmt.Println(p.Meal.Name)
    fmt.Println(p.Meal.Sweetness)
    fmt.Println()

    m := Meat{"Chicken", 10}

    /* error
    p = Person[Meat]{}
    p.Name = "John"
    p.Meal = m

    fmt.Println(p.Meal.Name)
    fmt.Println(p.Meal.Bloodiness)
    */
    
    
    p2 := Person[Meat]{}
    p2.Name = "John"
    p2.Meal = m

    fmt.Println(p2.Meal.Name)
    fmt.Println(p2.Meal.Bloodiness)
}

You should re declare Person

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.