I got a bug in Go when using an interface{}
as function parameter type, when given a non-pointer type, and using json.Unmarshal
with it.
Because a piece of code is worth a thousand words, here is an example:
package main
import (
"encoding/json"
"fmt"
)
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T
", i)
fmt.Printf("%T
", &i)
json.Unmarshal(j, &i)
fmt.Printf("%T
", i)
}
type Test struct {
Foo string
}
func main() {
test(Test{})
}
Which outputs:
main.Test
*interface {}
map[string]interface {}
json.Unmarshal
turns my struct to a map[string]interface{}
oO...
Little readings later explains some of it, interface{}
is a type in itself, not some sort of typeless container, which explains the *interface{}
, and the fact that json.Unmarshal
could not get the initial type, and returned a map[string]interface{}
..
From Unmarshal
docs:
To unmarshal JSON into an interface value, Unmarshal stores one of these in the interface value: [...]
And if I pass a pointer to the test function like so, it works:
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T
", i)
fmt.Printf("%T
", &i)
json.Unmarshal(j, i)
fmt.Printf("%T
", i)
fmt.Println(i)
}
func main() {
test(&Test{})
}
Which outputs:
*main.Test
*interface {}
*main.Test
&{bar}
Cool, the data is unmarshalled and all, now in this second snippet I removed the &
when calling Unmarshal
. Because I have a *Test
in i
, no use for it.
So in all logic, if I put back the &
to i
when calling Unmarshal
it should mess up with i
's type again. But no.
If I run:
func test(i interface{}) {
j := []byte(`{ "foo": "bar" }`)
fmt.Printf("%T
", i)
fmt.Printf("%T
", &i)
json.Unmarshal(j, &i)
fmt.Printf("%T
", i)
fmt.Println(i)
}
func main() {
test(&Test{})
}
Well it still works:
*main.Test
*interface {}
*main.Test
&{bar}
And now I'm out of google search queries.