Basierend auf Bildern von gopherize.me
Sehr oft müssen wir mit Go-Code mit verschiedenen HTTP-APIs arbeiten oder selbst als HTTP-Dienst fungieren.
Einer der häufigsten Fälle: Wir empfangen Daten in Form einer Struktur aus der Datenbank, senden die Struktur an die externe API, als Antwort erhalten wir eine andere Struktur, transformieren sie irgendwie und speichern sie in der Datenbank.
Mit anderen Worten: Eine solche Verarbeitung erfordert nicht viele separate Operationen mit den Anforderungs- und Antwortstrukturen.
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 .
Außerdem müssen Sie für den Typ die Codierungs- und Decodierungsfunktionen beschreiben
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
}
Als Ergebnis der Beseitigung von Referenztypen in der API entwickelte ich eine Nanobibliothek mit grundlegenden Nullable-Typen mit Codierungs- und Decodierungsfunktionen für JSON, jsoniter, easyjson, gocql.
Bequeme Verwendung von Nullable-Typen
Und eine der letzten Fragen, die Sie zum Wechsel zu Nullable-Typen stellen können, ist, ob es zweckmäßig ist, sie zu verwenden.
Meine persönliche Meinung ist praktisch, Typen haben das gleiche Verwendungsmuster wie variable Referenzen.
Wenn wir einen Link verwenden, schreiben wir
if a != nil && *a == "sometext" {
Mit einem Nullable-Typ schreiben wir
if a.Valid && a.String == "sometext" {