Go:参照タイプを使用せずにnil値を使用する方法



gopherize.meの画像に基づく



Goコードからは、さまざまなHTTP APIを操作するか、自分でHTTPサービスとして機能する必要があります。



最も一般的なケースの1つは、データベースから構造の形式でデータを受信し、その構造を外部APIに送信し、それに応じて別の構造を受信し、何らかの方法で変換してデータベースに保存することです。



言い換えると、このような処理では、要求構造と応答構造を使用した多くの個別の操作は必要ありません。



APIの要求構造と応答構造には、nilであり、nil以外の値をとることができるフィールドがあるのが普通です。そのような構造は通常このように見えます



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


また、これは参照型であるため、Goコンパイラーはエスケープ分析を実行し、指定された変数をヒープに転送できます。このような変数を頻繁に作成する場合、GCに余分な負荷がかかり、GCに使用済みメモリをすべて解放する時間がない場合は、「メモリリーク」が発生する可能性があります。



そのような状況で何ができるか:



  • nil値を使用しないように外部APIを変更します。これは許容できる場合もありますが、APIを変更することは必ずしも良い考えではありません。まず、余分な作業であり、次に、そのようなやり直しから発生する可能性のあるエラーです。
  • nil値を受け入れることができるように、Goコードを変更しますが、そのために参照タイプを使用しないでください。


, " "



.



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 .



また、タイプについては、エンコードおよびデコード機能を説明する必要があります。



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
}


APIで参照型を削除した結果、JSON、jsoniter、easyjson、gocqlのエンコードおよびデコード機能を備えた基本的なNullable型を備えたnanライブラリを開発しまし



Nullableタイプを使用する便利さ



そして、Nullableタイプへの切り替えについて尋ねることができる最後の質問の1つは、それらを使用するのが便利かどうかです。



私の個人的な意見は便利です。型は変数参照と同じ使用パターンを持っています。



リンクを使用する場合、



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


Nullableタイプでは、次のように記述します。



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



All Articles