Max*_*Max 3 type-inference go slice
我的代码调用一个库函数,大致如下:
func Search() ([]myLibrary.SomeObject, error) {
var results []apiv17.SomeObject
// ...
if (resultsFound) {
results = append(results, someResult)
}
return results
}
Run Code Online (Sandbox Code Playgroud)
...我的代码调用它,然后将其封送为 JSON。
results, err := myLibrary.Search()
bytes, err := json.Marshal(results)
Run Code Online (Sandbox Code Playgroud)
现在的问题是,由于函数的编写方式Search
(假设我们无法更改它),nil
如果没有结果,它将返回一个未初始化的切片。不幸的是,没有办法配置encoding/json
将 nil 切片编码为[]
(参见例如此提案以及正在进行的讨论)。
显式检查nil
解决了问题:
results, err := myLibrary.Search()
if results == nil {
results = []apiv17.SomeObject{}
}
bytes, err := json.Marshal(results)
Run Code Online (Sandbox Code Playgroud)
...但它还添加了对返回类型 的显式依赖apiv17.SomeObject
。这很不方便,因为该类型在库中经常更改。例如,在下一个库版本中可能是apiv18.SomeObject
.
通过nil
上述检查,每次发生这种情况时我都必须更新我的代码。
有没有办法避免这种情况,并在不显式引用变量类型的情况下为变量分配一个空的非零切片?像这样的东西:
results = [](type of results){}
Run Code Online (Sandbox Code Playgroud)
您可以使用通用函数来捕获切片的基本类型并返回长度为零的切片:
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 []
}
Run Code Online (Sandbox Code Playgroud)
v []T
需要常规参数的目的echo
是允许类型推断将切片[]apiv17.SomeObject
与参数统一[]T
并推断T
为基本类型apiv17.SomeObject
,以便您可以像调用它一样echo(n)
而不需要显式类型参数。
当然,该包apiv17
在编译时是已知的,因为它是通过 传递导入的myPackage
,因此您可以利用这一点和类型推断来避免为 添加显式import
语句apiv17
。
这就是多文件游乐场上的样子:https://go.dev/play/p/4ycTkaGLFpo
该类型在包中声明bar
,但main
仅导入play.ground/foo
且仅使用foo.GetFooBar
。
反射。只需将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))
}
Run Code Online (Sandbox Code Playgroud)
然后将指针传递给切片,以便您可以通过反射设置其值。
func main() {
n := foo.GetFooBar()
if n == nil {
set(&n)
}
fmt.Printf("type: %T, val: %v, is nil: %t\n", n, n, n == nil)
// type: []bar.FooBar, val: [], is nil: false
bytes, _ := json.Marshal(n)
fmt.Println(string(bytes)) // prints [] again
}
Run Code Online (Sandbox Code Playgroud)
Go 1.17 游乐场:https://go.dev/play/p/4jMkr22LMF7 ?v=goprev