22

I have a function:

func ReturnTuples(map_ map[interface{}]interface{}) [][]interface{} {

In which I'm trying to call like this:

m := make(map[string]int)
m["k1"] = 7
m["k2"] = 13
fmt.Println(ReturnTuples(m))

But I'm getting

cannot use m (type map[string]int) as type map[interface {}]interface {} in argument to ReturnTuples

Shouldn't it work since string and int both implement interface{}?

I've searched and the best I could find was Convert map[interface {}]interface {} to map[string]string but it won't answer why I cannot use m as an argument.

I also believe that if the argument of the function were only interface{} it would work too, since map[something][something] implements interface, right? What is the best way to do it, and why it won't work in my case?

halfer
  • 19,824
  • 17
  • 99
  • 186
  • 3
    https://golang.org/doc/faq#covariant_types – Volker Feb 23 '17 at 10:48
  • @Volker but I don't see the method type being wrong, I didn't even enforce the method type since I'm just printing the function's return value. The problem, as says the compiler, is on the argument type –  Feb 23 '17 at 10:58
  • 5
    The reason why you cannot pass `map[string]int` as that `map[interface{}]interaface{}` is that `string`, `int`, and `interface{}` all has different data structure. `string` is a single pointer which is maybe 64bit, `int` is maybe signed 64bit value, however `interface{}` is structure of two pointers named [emptyInterface](https://golang.org/src/reflect/value.go#L174) – ymonad Feb 23 '17 at 11:16
  • 2
    And the reason why you **can** pass `string` and `int` to `interface{}` is that golang's runtime is implicitly converting it using [runtime.convT2E()](https://golang.org/src/runtime/iface.go#L191) when needed. – ymonad Feb 23 '17 at 11:19
  • 3
    So that means that if you want to convert `map[string]int` to `map[interface{}]interface{}`, you have to call `runtime.convT2E` to all the keys an values, of cause you can do it if you write your code to do that, but golang's runtime just don't do it automatically. – ymonad Feb 23 '17 at 11:20
  • @ymonad thanks, I understood. So I should use map_ interface{} as an argument? –  Feb 23 '17 at 11:54
  • @GuerlandoOCs It depends on what you are trying to do in `ReturnTuples()`, maybe you can use reflection, or sometime define your interface instead, or there's another clever way to do that. – ymonad Feb 23 '17 at 12:10

2 Answers2

12

A solution to your problem, simply initiate the map as an empty interface of empty interfaces:

m := map[interface{}]interface{}

then you can assign any type key or value you want in the 'ReturnTuples' function.

playground example

NOTE: remember that if you want to use the values later as the original types, you will need to use type assertion because now they are of type interface{}

You may do something this like this, were anything is one map value which you can get using a for loop:

switch v := anything.(type) {
      case string:
            fmt.Println(v)
      case int32, int64:
            fmt.Println(v)
      case string:
            fmt.Println(v)
      case SomeCustomType:
            fmt.Println(v)
      default:
            fmt.Println("unknown")
}

If you are looking for an explanation for the "why" @ymonad gave a full answer so I wont repeat it again.

hope it make sense

PS: don't get the down votes on the question, a legit one in my eyes...

Community
  • 1
  • 1
Blue Bot
  • 2,278
  • 5
  • 23
  • 33
  • 1
    Thank you, that's exactly what I needed. And yes, I'm tired of these downvotes. Everything is downvoted now... –  Feb 24 '17 at 03:47
  • Looks like the playground link no longer works due to "Unavailable For Legal Reasons". – Stephen L Nov 23 '17 at 21:40
1

You can type assert in the function itself.

func test(m interface{},key interface{}) bool { // Map is passed as reference
        ma := m.(map[interface{}]interface{})
        if _, ok := ma[key]; ok == false {
        ....
}