0

Is it possible in Go to have a function parameter of a type that satisfies multiple types? I know you can use interfaces to pass in various types, but let me give an example.

Say I have the following three types:

type thing struct {
    name string
}

type barThing struct {
    thing
    barAttr string
}

type fooThing struct {
    thing
    fooAttr int
}

I'd like to have a single functions such that I can pass in a map[string]<anyOfThoseTypes> and add each map item's key to the name field. Something like:

func copyKeysIntoStruct(m map[string]thing) {
    for k, v := range m {
        v.name = k
        m[k] = v
    }
}

func main() {
    bars := map[string]barThing{
        "b1": {thing: thing{name: "overwrite"}},
        "b2": {thing: thing{}},
    }
    foos := map[string]fooThing{
        "f1": {thing: thing{}},
        "f2": {thing: thing{name: "goingaway"}},
    }
    bars = copyKeysIntoStruct(bars)
    fmt.Println(bars)
    foos = copyKeysIntoStruct(foos)
    fmt.Println(foos)
}

However, here I get cannot use bars (type map[string]barThing) as type map[string]thing in argument to copyKeysIntoStruct, which makes sense. However, I don't want to write this same function for each type that has thing embedded.

The other possibility was to take m map[string]interface{} as an argument and use a type switch and assertions to get the right behavior, but that seemed to be a NOGO too.

In summary, I'm looking to have inputs like:

    bars := map[string]barThing{
        "b1": {thing: thing{name: "overwrite"}},
        "b2": {thing: thing{}},
    }
    foos := map[string]fooThing{
        "f1": {thing: thing{}},
        "f2": {thing: thing{name: "goingaway"}},
    }

and outputs like:

    bars := map[string]barThing{
        "b1": {thing: thing{name: "b1"}},
        "b2": {thing: thing{name: "b2"}},
    }
    foos := map[string]fooThing{
        "f1": {thing: thing{name: "f1"}},
        "f2": {thing: thing{name: "f2"}},
    }
theherk
  • 6,954
  • 3
  • 27
  • 52
  • This can't be done for the same reasons expressed in [Type converting slices of interfaces in go](http://stackoverflow.com/questions/12753805/type-converting-slices-of-interfaces-in-go). You'd need to use `map[string]yourInterface`, not `map[string]thing`, `map[string]barThing`, or `map[string]fooThing`, and all of `thing`, `barThing`, and `fooThing` would need to implement `yourInterface`. That would allow you to do what you want, though you'd want a type switch to convert from the interface type to one of the types mentioned; you could use `case thing, barThing, fooThing: v.name = k`. –  Apr 26 '17 at 02:48
  • Cools. Thanks for the reply. I did have a working implementation using the `map[string]interface`, but then we're back to implementing the same method to satisfy the interface for each type again. It just isn't DRY, but that seems to be the price we pay for type safety. – theherk Apr 26 '17 at 04:15
  • I was slightly mistaken in my last comment. You can create an interface requiring a `setName(string)` method, abstracting the assignment operation. `thing` could implement the method, and the other types with `thing` embedded could use that same method without any need to duplicate the method for the other types, thanks to how composition works. Then your map would be as I described, requiring the interface rather than any of the struct types. If `thing` wasn't embedded in the other types, you'd be forced use a type switch, or abandon the DRY principle in this particular case. –  Apr 26 '17 at 15:07

0 Answers0