Go: Como usar valores nulos sem usar tipos de referência



Com base em imagens de gopherize.me



Muitas vezes, a partir do código Go, temos que trabalhar com várias APIs HTTP ou agir como um serviço HTTP.



Um dos casos mais comuns: recebemos dados em forma de estrutura do banco de dados, enviamos a estrutura para a API externa, em resposta recebemos outra estrutura, de alguma forma transformamos e salvamos no banco de dados.



Em outras palavras: esse processamento não requer muitas operações separadas com as estruturas de solicitação e resposta.



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 .



Além disso, para o tipo, você precisará descrever as funções de codificação e decodificação.



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
}


Como resultado de me livrar dos tipos de referência na API, desenvolvi uma biblioteca nan , com tipos Nullable básicos com funções de codificação e decodificação para JSON, jsoniter, easyjson, gocql.



Conveniência de usar tipos anuláveis



E uma das últimas perguntas que você pode fazer sobre a mudança para tipos anuláveis ​​é se eles são convenientes de usar.



Minha opinião pessoal é conveniente, os tipos têm o mesmo padrão de uso que as referências de variáveis.



Ao usar um link, escrevemos



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


Com um tipo anulável, escrevemos



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



All Articles