Recently, I got exposed to following source code from "Go Programming Blueprints" book.
type googleGeometry stuct {
*googleLocation `json:"location"`
}
type googleLocation struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
}
What I don't understand is why googleGeometry structure uses pointer instead of literal, and what is the reason behind such declaration?
I would expect below declaration instead of a pointer.
type googleGeometry stuct {
gl googleLocation `json:"location"`
}
googleGeometry
embeds a pointer to a googleLocation
. It is essentially an unnamed field so that the Lat
and Lng
fields are accessible as if they were top level fields.
Why use
type googleGeometry stuct {
*googleLocation `json:"location"`
}
instead of
type googleGeometry stuct {
googleLocation `json:"location"`
}
?
I think they made the wrong call here. The pointer is nillable so if you do:
g := googleGeometry{}
fmt.Println(g.Lat)
you will get a nil reference panic. If you embed a non-pointer struct, the fields will be automatically initialized to zeros.
I suppose it is because location
can be null
or would be absent at all.
Such cases can be represented with pointers. Because if it would be literal you will always have default values for lat
and lng
.
Please take a look on this example: Why pointer?
And this with a value type instead of reference: Value types always have default values
I'm not sure about the entire context of the question, however when a pointer is embedded inside the struct, even when the variable of type googleGeometry
is passed by value the embedded googleLocation
pointer still points to the same memory address as the initial variable (as the address is simply copied). Therefore although the original struct is passed by value both original and the copied variables share the same embedded pointer. That might be the intended behaviour.
The main reason is JSON (de)serialization. If you want to unmarshal JSON into a struct and validate if certain attributes are present in the document using a pointer is a convenient way. Since the unmarshaller will leave missing fields nil
.
The following code code will print: missing location attribute
func main() {
doc := []byte("{}") // json that misses a location member
var geometry googleGeometry
json.Unmarshal(doc, &geometry)
if geometry.googleLocation == nil {
fmt.Println("missing location attribute")
} else {
fmt.Println("location attribute unmarshalled correctly")
}
}