Go: How to use nil values ​​without using reference types



Based on images from gopherize.me



Quite often from Go code we have to work with various HTTP APIs or act as an HTTP service ourselves.



One of the most common cases: we receive data in the form of a structure from the database, send the structure to the external API, in response we receive another structure, somehow transform it and save it to the database.



In other words: such processing does not require many separate operations with the request and response structures.



API , , nil - -nil .



type ApiResponse struct {
  Code *string json:"code"`
}


, , Go escape . β€” GC " ", GC .



:



  • API , nil . , API β€” : -, , - β€” , .
  • Go , nil , .


, " "



.



Go



type pointerSmall struct {
 Field000 *string
 Field001 *string
 Field002 *string
 Field003 *string
 Field004 *string
 Field005 *string
}


,



type valueSmall struct {
 Field000 string
 Field001 string
 Field002 string
 Field003 string
 Field004 string
 Field005 string
}


0 , .

, .



: Go, ( - ) .



β€” . , . . β€” . , .. Go .



β€” , . , .



BenchmarkPointerSmall-8    1000000000          0.295 ns/op        0 B/op        0 allocs/op
BenchmarkValueSmall-8      184702404          6.51 ns/op        0 B/op        0 allocs/op


. , - - .



BenchmarkPointerSmallChain-8    1000000000          0.297 ns/op        0 B/op        0 allocs/op
BenchmarkValueSmallChain-8      59185880         20.3 ns/op        0 B/op        0 allocs/op


JSON . , jsoniter. . , .



BenchmarkPointerSmallJSON-8       49522      23724 ns/op    14122 B/op       28 allocs/op
BenchmarkValueSmallJSON-8         52234      22806 ns/op    14011 B/op       15 allocs/op


, easyjson. , .



BenchmarkPointerSmallEasyJSON-8       64482      17815 ns/op    14591 B/op       21 allocs/op
BenchmarkValueSmallEasyJSON-8         63136      17537 ns/op    14444 B/op       14 allocs/op


: , . (/ ) β€” .



.



type pointerBig struct {
 Field000 *string
 ...
 Field999 *string
}

type valueBig struct {
 Field000 string
 ...
 Field999 string
}


. , 0 , ( , .. ). , :



BenchmarkPointerBig-8       36787      32243 ns/op    24192 B/op     1001 allocs/op
BenchmarkValueBig-8        721375       1613 ns/op        0 B/op        0 allocs/op


. . ( , ).



BenchmarkPointerBigChain-8       36607      31709 ns/op    24192 B/op     1001 allocs/op
BenchmarkValueBigChain-8        351693       3216 ns/op        0 B/op        0 allocs/op


.



BenchmarkPointerBigJSON-8         250    4640020 ns/op  5326593 B/op     4024 allocs/op
BenchmarkValueBigJSON-8           270    4289834 ns/op  4110721 B/op     2015 allocs/op


, easyjson. . , jsoniter.



BenchmarkPointerBigEasyJSON-8         364    3204100 ns/op  2357440 B/op     3066 allocs/op
BenchmarkValueBigEasyJSON-8           380    3058639 ns/op  2302248 B/op     1063 allocs/op


: β€” , . β€” " ". (easyjson ), β€” .





β€” Nullable . sql β€” sql.NullBool, sql.NullString .



Also, for the type, you will need to describe the encoding and decoding functions.



func (n NullString) MarshalJSON() ([]byte, error) {
    if !n.Valid {
        return []byte("null"), nil
    }

    return jsoniter.Marshal(n.String)
}

func (n *NullString) UnmarshalJSON(data []byte) error {
    if bytes.Equal(data, []byte("null")) {
        *n = NullString{}
        return nil
    }

    var res string

    err := jsoniter.Unmarshal(data, &res)
    if err != nil {
        return err
    }

    *n = NullString{String: res, Valid: true}

    return nil
}


As a result of getting rid of reference types in the API - I developed a nan library , with basic Nullable types with encoding and decoding functions for JSON, jsoniter, easyjson, gocql.



Convenience of using Nullable types



And one of the last questions you can ask about switching to Nullable types is whether they are convenient to use.



My personal opinion is convenient, types have the same usage pattern as variable references.



When using a link, we write



if a != nil && *a == "sometext" {


With a Nullable type, we write



if a.Valid && a.String == "sometext" {



All Articles