我的代码调用了一个库函数,它看起来大致如下:
func Search() ([]myLibrary.SomeObject, error) {
var results []apiv17.SomeObject
// ...
if (resultsFound) {
results = append(results, someResult)
}
return results
}
…我的代码调用它,然后警察JSON。
results, err := myLibrary.Search()
bytes, err := json.Marshal(results)
现在的问题是,由于Search
函数的编写方式(让我们假设我们不能改变它),如果没有结果,它将返回一个未初始化的nil
片。不幸的是,没有办法将encoding/json
配置为将nil片编码为[]
(参见正在进行讨论的提案)。
显式检查nil
解决了这个问题:
results, err := myLibrary.Search()
if results == nil {
results = []apiv17.SomeObject{}
}
bytes, err := json.Marshal(results)
…但它也增加了对返回类型apiv17.SomeObject
的显式依赖。这很不方便,因为该类型在库中经常更改。例如,在下一个库版本中,它可能是apiv18.SomeObject
。
使用上面的nil
检查,每次发生这种情况时,我都必须更新我的代码。
是否有任何方法可以避免这种情况,并在不显式引用其类型的情况下为变量分配一个空的非nil切片?像这样:
results = [](type of results){}
Go 1.18
可以使用一个泛型函数捕获切片的基类型并返回长度为0的切片:
func echo[T any](v []T) []T {
return make([]T, 0)
}
func main() {
n := foo.GetFooBar()
if n == nil {
n = echo(n) // no need to refer to apiv17 here
}
bytes, _ := json.Marshal(n)
fmt.Println(string(bytes)) // prints []
}
在echo
中要求一个常规参数v []T
的目的是允许类型推断将片[]apiv17.SomeObject
与参数[]T
统一起来,并将T
推断为基本类型apiv17.SomeObject
,这样您就可以像echo(n)
一样调用它,而不需要显式的类型参数。
包apiv17
在编译时当然是已知的,因为它是通过myPackage
传入的,所以您可以利用这一点和类型推断来避免为apiv17
添加显式的import
语句。
这是它在多文件游乐场上的样子:https://go.dev/play/p/4ycTkaGLFpo
类型是在bar
包中声明的,但是main
只导入play.ground/foo
并且只使用foo.GetFooBar
。
Go 1.17及以下
反射。只需将echo
函数从上面更改为接受interface{}
参数(在Go 1.17中没有any
,还记得吗?)并使用reflect.MakeSlice
执行此操作:
func set(v interface{}) {
rv := reflect.ValueOf(v)
if rv.Kind() != reflect.Ptr {
panic("not a ptr")
}
reflect.Indirect(rv).Set(reflect.MakeSlice(rv.Type().Elem(), 0, 0))
}
然后传递一个指向切片的指针,这样你就可以通过反射来设置它的值。
func main() {
n := foo.GetFooBar()
if n == nil {
set(&n)
}
fmt.Printf("type: %T, val: %v, is nil: %tn", n, n, n == nil)
// type: []bar.FooBar, val: [], is nil: false
bytes, _ := json.Marshal(n)
fmt.Println(string(bytes)) // prints [] again
}
Go 1.17 playground: https://go.dev/play/p/4jMkr22LMF7?v=goprev
另一个答案描述了如何创建一个空片。
但是您可以更简单地解决原始问题:如果results
是nil
,则不需要创建空片,无论它具有什么元素类型,JSON封送都将是[]
。因此,如果results
是nil
,则不需要调用json.Marshal()
,只需输出"[]
:
results, err := myLibrary.Search()
var bytes []byte
if results == nil {
bytes = []byte{'[', ']' } // JSON marshaling result is "[]"
} else {
bytes, err = json.Marshal(results)
// Handle error
}