Ir: Cómo usar valores nulos sin usar tipos de referencia



Basado en imágenes de gopherize.me



Muy a menudo, a partir del código Go, tenemos que trabajar con varias API HTTP o actuar como un servicio HTTP nosotros mismos.



Uno de los casos más comunes: recibimos datos en forma de estructura desde la base de datos, enviamos la estructura a la API externa, en respuesta recibimos otra estructura, de alguna manera la transformamos y la guardamos en la base de datos.



En otras palabras: dicho procesamiento no requiere muchas operaciones separadas con las estructuras de solicitud y respuesta.



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 .



Además, para el tipo, deberá describir las funciones de codificación y decodificación.



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 deshacerme de los tipos de referencia en la API, desarrollé una biblioteca nan , con tipos básicos que aceptan valores NULL con funciones de codificación y decodificación para JSON, jsoniter, easyjson, gocql.



Conveniencia de usar tipos que aceptan valores NULL



Y una de las últimas preguntas que puede hacer sobre el cambio a tipos que aceptan valores NULL es si son convenientes de usar.



Mi opinión personal es conveniente, los tipos tienen el mismo patrón de uso que las referencias variables.



Cuando usamos un enlace, escribimos



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


Con un tipo anulable, escribimos



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



All Articles