Lei Zhilong

The best way to input is to output

Jun 4, 2020 - 4 minute read - Comments - Cheat Sheets

Golang

Disclaimer: This article will be updated from time to time to add new content or make any changes in exsisting sections without any notice. Using them under your own investigation in production is advised.

I. Grammar

1.1 Ommit empty struct in `json.Marshal()'

Suppose we have a nested struct like:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type Example struct {
  Info          string `json:"info,omitempty"`
  ExampleValues struct {
    DayOfMonth      int      `json:"dayOfMonth,omitempty"`
    DateStr         string   `json:"dateStr,omitempty"`
    SomeRandomFloat float64  `json:"someRandomFloat,omitempty"`
    StrArray        []string `json:"strArray,omitempty"`
    IsWorking       bool     `json:"isWorking,omitempty"`
  } `json:"exampleValues,omitempty"`
  }

And if only info filed is assinged with a value when initialized.

1
2
b, _ := json.Marshal(&Example{Info: "hello"})
fmt.Println(string(b))

What we get will be:

1
{ "info": "hello", "exampleValues": {} }

How do we omit ExampleValues field if it is not assigned with a value? Use a pointer!

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
type Example struct {
  Info          string `json:"info,omitempty"`
  ExampleValues *struct { // use a pointer here
    DayOfMonth      int      `json:"dayOfMonth,omitempty"`
    DateStr         string   `json:"dateStr,omitempty"`
    SomeRandomFloat float64  `json:"someRandomFloat,omitempty"`
    StrArray        []string `json:"strArray,omitempty"`
    IsWorking       bool     `json:"isWorking,omitempty"`
  } `json:"exampleValues,omitempty"`
}

And the result will be:

1
{ "info": "hello" }

1.2 Avoid omitempty for fields that are asgined with 0 or empty string

With a struct defined like this:

1
2
3
4
5
type TestStruct struct {
  BoolVar      bool        `json:"bool_var,omitempty"`
  IntVar       int         `json:"int_var,omitempty"`
  StringVar    string      `json:"string_var,omitempty"`
}

Even if we can initialize a TestStruct with some special values and perform a json marshal.

1
2
3
4
5
6
7
bb, _ := json.Marshal(&TestStruct{
  BoolVar:   false,
  IntVar:    0,
  StringVar: "",
})

fmt.Println(string(bb))

What we get will be an empty json object.

1
{}

And that might not be something we expected. Since the tag omitempty means all empty values (that are false, 0, any nil pointer or interface value, and any array, slice, map, or string of length zero) will be omited no matter whether thay are assigned on purpose or not. Sometimes what we need is some kind of feature like ommitnil tag that omit those fields which are not initialized and keep them even if they are initialized with empty values

Apparently there is no such tag feature in standard library. To make it work we may have to use interface{}.

1
2
3
4
5
6
7
8
type TestStruct2 struct {
  BoolVar          bool        `json:"bool_var,omitempty"`
  IntVar           int         `json:"int_var,omitempty"`
  StringVar        string      `json:"string_var,omitempty"`
  InterfaceBoolVar interface{} `json:"interface_bool_var,omitempty"`
  InterfaceIntVar  interface{} `json:"interface_int_var,omitempty"`
  InterfaceStrVar  interface{} `json:"interface_str_var,omitempty"`
}

To compare, we may initialize TestStruct2 twice in different ways, partly and wholly.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
partlyInitialized, _ := json.Marshal(&TestStruct2{
  BoolVar:   false,
  IntVar:    0,
  StringVar: "",
})

whollyInitialized, _ := json.Marshal(&TestStruct2{
  BoolVar:          false,
  IntVar:           0,
  StringVar:        "",
  InterfaceBoolVar: false,
  InterfaceIntVar:  0,
  InterfaceStrVar:  "",
})

fmt.Println(string(partlyInitialized))
fmt.Println(string(whollyInitialized))

And we get will be:

1
2
{}
{"interface_bool_var":false,"interface_int_var":0,"interface_str_var":""}

The result lies in the fact that interface{} won’t be nil after being initialized even if the data it points to is empty value.

1.3 Type Assertions & Type Switches

Interfaces in Go can introduce ambiguity about the underlying type. A type assertion allows us to extract the interface value’s underlying concrete value using this syntax: interfaceVariable.(concreteType).

For example:

1
2
var input interface{} = 12
number := input.(int)

NOTE: this will cause a panic if the interface variable does not hold a value of the concrete type.

We can test whether an interface value holds a specific concrete type by making use of both return values of the type assertion: the underlying value and a boolean value that reports whether the assertion succeeded. For example:

1
str, ok := input.(string) // no panic if input is not a string

If input holds a string, then str will be the underlying value and ok will be true. If input does not hold a string, then str will be the zero value of type string (ie. "" - the empty string) and ok will be false. No panic occurs in any case.

A type switch can perform several type assertions in series. It has the same syntax as a type assertion (interfaceVariable.(concreteType)), but the concreteType is replaced with the keyword type. Here is an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
var i interface{} = 12 // try: 12.3, true, int64(12), []int{}, map[string]int{}

switch v := i.(type) {
case int:
    fmt.Printf("the integer %d\n", v)
case string:
    fmt.Printf("the string %s\n", v)
default:
    fmt.Printf("type, %T, not handled explicitly: %#v\n", v, v)
}

II. Design Pattern

2.1 Singleton

Singleton is usually used as a global instance, initialized lazily or eagerly with double concurrent check.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
package singleton

import "sync"

var (
	lazySingleton *Singleton
	once          = &sync.Once{}
)

// GetLazyInstance
func GetLazyInstance() *Singleton {
	if lazySingleton == nil {
		once.Do(func() {
			lazySingleton = &Singleton{}
		})
	}
	return lazySingleton
}