From Golang Slice Initialization

This post starts from a small issue of serializing uninitialized slice and step around to highlight and recap slice, array and rune topics.

Uninitialized Slice

The issue starts with an uninitialized slice in a regular PR review. For example, take a sample RESTful handler for listing operation ("get" on collection HTTP endpoint) and the code just use a defined slice but it is uninitialized by default. I believe it's tested in normal path with results, either in UT or SIT. However, when we take a corner case if no resource is found from the query, the RESTful shall return {"items":[]}.

var s []string
for _, item := range someResult {
s = append(s, item)
}
res, _ := json.Marshal(struct{ Items []string }{Items: s})
fmt.Printf("res = %s\n", res)

In above simplified sample, the uninitialized slice is actually encoded to null. In this case, if someResult is empty, the output will be res = {"Items":null}. It's sometimes unfriendly to downstream APIs.

Initialized Slice

There are three reference types in golang: slice, chan and map. The default value of them will be nil if the vars are not initialized. Among them slice and map are widely used in models for marshalling and unmarshalling. To encode the empty slice or map to an empty collection, the vars could be initialized before using. Golang provides sugar for slice, map and chan to use make to get a pointer to initialized value. Here the pointer is actually a reference though there is no such reference type explicitly. It means when passing a map in to a method as an argument, the method could update the value via this var and the var address is same as the input var. The later point is different when passing a point of some var. If *int is passed as an argument, the *int itself is copied so the address of the pointer itself is different within the method but the method could update the values this pointer is pointing to.

var s []string = make([]string, 0) // or s:= []string{}
for _, item := range someResult {
s = append(s, item)
}
res, _ := json.Marshal(struct{ Items []string }{Items: s})
fmt.Printf("res = %s\n", res)

If the query result is empty, it will still print res = {"Items":[]}. Same tips on map, an uninitialized reference type will be marshalled to "null" not an empty value.

Rune, byte and string

TBC

Change Log

Oct, 2021: Initial post draft.