分配空切片而不引用其类型?

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)

bla*_*een 5

去1.18

您可以使用通用函数来捕获切片的基本类型并返回长度为零的切片:

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


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))
}
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