为了账号安全,请及时绑定邮箱和手机立即绑定

在不参考其类型的情况下分配空切片?

在不参考其类型的情况下分配空切片?

Go
红糖糍粑 2023-01-03 11:29:09
我的代码调用了一个大致如下所示的库函数: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上面的检查,每次发生这种情况时我都必须更新我的代码。有什么办法可以避免这种情况,并在不显式引用其类型的情况下为变量分配一个空的、非零的切片?是这样的:results = [](type of results){}
查看完整描述

2 回答

?
POPMUISE

TA贡献1765条经验 获得超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 []

}

需要常规参数v []Tin的目的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.


转到 1.17 及以下版本

反射。只需将echo上面的函数更改为接受interface{}参数(anyGo 1.17 中没有,还记得吗?),然后执行以下操作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: %t\n", n, n, n == nil)

    // type: []bar.FooBar, val: [], is nil: false

    

    bytes, _ := json.Marshal(n)

    fmt.Println(string(bytes)) // prints [] again

}

Go 1.17 游乐场:https ://go.dev/play/p/4jMkr22LMF7?v=goprev


查看完整回答
反对 回复 2023-01-03
?
慕斯王

TA贡献1864条经验 获得超2个赞

另一个答案描述了如何创建一个空切片。


但是你可以更简单地解决你原来的问题:如果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

}


查看完整回答
反对 回复 2023-01-03
  • 2 回答
  • 0 关注
  • 86 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号