Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

为什么find函数里必须传入slice的指针呢,slice本身不就是引用类型吗 #7030

Open
espoirr opened this issue May 20, 2024 · 8 comments
Assignees
Labels

Comments

@espoirr
Copy link

espoirr commented May 20, 2024

slice本来就是引用类型,为什么还需要传入指针
var students []dao.Student
db.Find(students)
这样写会爆错
var students []dao.Student
db.Find(&students)
这也就不会
但是map就可以不传指针
result := map[string]interface{}{}
db.Model(&dao.Student{}).Find(result)
map和slice同为引用类型,为什么find函数中使用方式却不同

@espoirr espoirr added the type:question general questions label May 20, 2024
@github-actions github-actions bot added type:missing reproduction steps missing reproduction steps and removed type:question general questions labels May 20, 2024
Copy link

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.ioSearch Before Asking

@ivila
Copy link

ivila commented May 21, 2024

@espoirr 先给个结论,逻辑上(即Golang的设计来说)来说你都要传指针,当前Golang的实现上你的map可以不传指针。

先解答你的问题,为什么slice是个引用类型,却需要传入指针,而同为引用类型的map却不需要。
这是因为实际上在编译后,函数传递时,Slice传递的是个SliceHeader一样的结构体,你可以看这里,而map是传递了变量的地址。
但注意的是,这些都是当前实现上的事情。

然后再说一个概念,从设计上来说,这里不是在传入指针,而是在传入引用(可以看看C#的ref,out等关键字设计,能让你对这些设计上的东西有些了解),也就是约定上这个函数内部可以对传入的变量做修改。假如你传递的并非引用,那实际上约定来说我就不该动你的东西。
所以当你的slice传递的不是引用时,我爱怎么实现都可以(比如当前Golang的实现就是传入一份struct的Copy),也就导致了你的疑问。(对于这些问题有兴趣的可以看看C++的undefined behavior,C++这种问题比较多)

然后当前Golang编译上,map传递变量地址我没有找到设计说明(就是保证一定传递的是地址,可能是没有,也可能是我没看到),我个人觉得当前的编译后传递地址的原因是因为map的实现太大了,传递拷贝会影响性能,当然这只是我无知的猜测,不过如果我猜测是对的话,你用map时没有传递引用,实际上也是各undefined behavior,哪天跑不了了也是可能的。

@thinkgos
Copy link

仔细看下slice和map的实现. 实际map持有的是指针,指向实现, 传map可以修改原始数据. 而slice并不是, 它实际是数组指针, 这个切片的长度, 容量. 你不传指针的方式进去, 底层无法修改原始数据. 那么你什么也得不到. 找篇传门介绍slice的文章看看原理就很容易明白

Copy link

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.ioSearch Before Asking

@espoirr
Copy link
Author

espoirr commented May 21, 2024

@ivila @thinkgos
我的理解是,gorm在find查询到结果,将结果的底层数组重新赋值给传入的引用,在函数内改变引用的指向,就需要传递引用的指针

Copy link

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.ioSearch Before Asking

@ivila
Copy link

ivila commented May 21, 2024

@ivila @thinkgos 我的理解是,gorm在find查询到结果,将结果的底层数组重新赋值给传入的引用,在函数内改变引用的指向,就需要传递引用的指针

@espoirr 这个理解是错的,从实现上来说,gorm没有修改引用的指向,而是直接在原值上做了更改(通过反射的方式,根据指针找到了slice这个结构体的地址并操作原值修改,就算底层用了Assign,实际编译结果也是找到原值做修改)。
或者说你以为的slice是这样:

var a []int // 你认为这个是个指针类型,等同于指针
methodCall(a)  // 你认为这里应该传入自动传入指指针

实际上slice编译后是这样

var a struct {  // 实际上这里是个结构体类型
    Data uintptr
    Len  int
    Cap  int
}
methodCall(a)  // 你传入的是个结构体

这种从“实现”上说明的方式,可能你会好理解一点。

然后对于map来说

var m map[string]interface{} //这里是声明了一个hmap的指针,看makemap函数返回值(https://github.com/golang/go/blob/22344e11f27d9667e7bbb6209df59e9a9e976d91/src/runtime/map.go#L297)
methodCall(m) // 传入的是个 *hmap指针

实际实现长这样

var m *hmap  // 这个是个指针类型
methodCall(m)

但是如果我上面说的,不要过多去关注实现上的东西,而是去关注设计上的东西。
毕竟实现与设计不同算Bug,有实现但没设计叫undefine behavior,工程上这两个我们都不想要。

Copy link

The issue has been automatically marked as stale as it missing playground pull request link, which is important to help others understand your issue effectively and make sure the issue hasn't been fixed on latest master, checkout https://github.com/go-gorm/playground for details. it will be closed in 30 days if no further activity occurs. if you are asking question, please use the Question template, most likely your question already answered https://github.com/go-gorm/gorm/issues or described in the document https://gorm.ioSearch Before Asking

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

4 participants