1. JSON four-state problem
When parsing JSON through the standard library in Go, we often distinguish between four states for a single field:
- A non-zero value exists
- Exists a value of 0
- There is null
- missing
Of the four states, null and missing can generally be considered the same state, but missing values for 0 values have to be padded with 0 values due to type-safe language features. This can cause judgment invalidation for some services that default to a value of 0. For example, in Go, a field is usually defined as int, and when an HTTP body is parsed, the field is also parsed to a value of zero if it was not present in the original JSON. However, due to the traditional practice of code=0 as a successful response, it is easy to misjudge the error and lead to bugs. A more common practice is to define fields as *int, which will be filled to nil if the field value does not exist, distinguishing the zero value from the missing state.
2. JSON String Number problem
In JavaScript, the maximum value of the Number type is 2 to the power of 53. Exceeding this Number will cause loss of precision. All you need to do is tag the field to support converting from String to INT64. The following is an example:
type Foo struct {
A int64 `json:"a,string"`
}
func main(a){
rawString := `{"a": "1"}`
var foo Foo
if err := json.Unmarshal([]byte(rawString), &foo); err ! =nil {
panic(err)
}
fmt.Printf("int: %+v\n", foo)
}
Copy the code
JSON int type assertion problem
If you get JSON like this: {“foo”: 1} But you don’t want to write a Struct specifically for parsing, you want to use a map[string]interface{} and then assert foo as an int. Json. The Unmarshal library defaults the number value to float64, causing the assertion to fail.
4. Map capacity expansion problem
We know that during map initialization, memory space can be pre-allocated through the make function, and automatic expansion will be performed when new keys are added in the map. However, as of now (go 1.16), the space occupied by buckets in the Go Map will not automatically shrink and will not be GC, which can even cause OOM. See #20135 for more details. The following code will print the memory occupied by each phase:
func main(a) {
v := struct{}{}
a := make(map[int]struct{})
for i := 0; i < 100000; i++ {
a[i] = v
}
runtime.GC()
printMemStats("After Map Add 1000000")
for i := 0; i < 100000- 1; i++ {
delete(a, i)
}
runtime.GC()
printMemStats("After Map Delete 99999")
for i := 0; i < 100000- 1; i++ {
a[i] = v
}
runtime.GC()
printMemStats("After Map Add 99999 again")
fmt.Printf("%d\n".len(a))
a = nil
runtime.GC()
printMemStats("After Map Set nil")}func printMemStats(mag string) {
var m runtime.MemStats
runtime.ReadMemStats(&m)
fmt.Printf("%v: memory = %vKB, GC Times = %v\n", mag, m.Alloc/1024, m.NumGC)
}
Copy the code
After Map Add 1000000: memory = 1625KB, GC Times = 1 After Map Delete 99999: Memory = 1628KB, GC Times = 2 After Map Add 99999 again: memory = 1628KB, GC Times = 3 100000 After Map Set nil: memory = 117KB, GC Times = 4Copy the code
As you can see, memory usage is still high after deletion and GC. This situation should be especially noted in the use of maps with long life cycles. The official recommendation is, new, new.
5. Null value judgment problem of nil and interface nil
Define a variable with a null value of some type, assign the variable to an interface, and you will find that the null value judgment fails. This question comes from: Golang-Nuts
type someType struct{ f1 string }
func main(a) {
var v *someType
var v2 interface{}
v2 = v
fmt.Printf("v2 == nil: %t\n", v2 == nil)
fmt.Printf("v2 reflected val is nil: %t\n", reflect.ValueOf( v2 ).IsNil() )
}
Copy the code
V2 == nil, v2 == nil, v2 == nil, v2 == nil, v2 == nil, v2 == nil, v2 == nil, So there’s a lapse of judgment. So be very careful with interfaces, and try not to pass a nil value of a certain type into an interface.
6. Default time zone problem
If a string contains no time zone information, it defaults to UTC, and time.Now defaults to the current system time zone. If you expect the current time zone, you may cause a time error.
func main(){
format := "2006-01-02 15:04"
t, _ := time.Parse(format, "2021-05-10 14:00")
fmt.Println(t)
fmt.Println("现在是: ", time.Now())
}
Copy the code
Output result:
2021-05-10 14:00:00 +0000 UTC
现在是: 2021-05-10 14:41:01.241168 +0800 CST m=+0.000088626
Copy the code
As you can see, the specified string with no time zone is parsed to the UTC time zone, but the time generated by now is the system time zone. You need to be wary of string times without time zones.
conclusion
This is the author’s daily summary of some experience, if there are mistakes welcome to correct. Wish everyone can write bug free code!