基于来自gopherize.me的图像
通常,从Go代码中,我们必须使用各种HTTP API或自己充当HTTP服务。
最常见的情况之一:我们从数据库接收结构形式的数据,将结构发送到外部API,作为响应,我们接收到另一个结构,以某种方式对其进行转换并将其保存到数据库中。
换句话说:这种处理不需要对请求和响应结构进行许多单独的操作。
API通常在请求和响应结构中具有可以为nil并且可以采用某些非nil值的字段。这样的结构通常看起来像这样
type ApiResponse struct {
Code *string json:"code"`
}
并且,由于它是引用类型,因此Go编译器可以进行转义分析,并且可以将给定的变量传输到堆中。在频繁创建此类变量的情况下,如果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 .
另外,对于类型,您将需要描述编码和解码功能。
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中的引用类型,我开发了一个nan库,该库具有基本的Nullable类型,这些类型具有JSON,jsoniter,easyjson和gocql的编码和解码功能。
使用Nullable类型的便利
关于切换到Nullable类型,您可能要问的最后一个问题是使用它们是否方便。
我个人认为很方便,类型与变量引用具有相同的用法模式。
当使用链接时,我们写
if a != nil && *a == "sometext" {
使用Nullable类型,我们写
if a.Valid && a.String == "sometext" {