新手最近在学习 Go,学习的时候有个小问题这几天我一直觉得很困扰。。求助一下大佬们

2023-01-10 00:44:15 +08:00
 gitlight

Q:当编写实体的 CRUD 接口的时候,到底是向上层传指针呢,还是传实体? 举个例子:

// 实体
type person struct {
	ID   uint
	name string
	age  uint
}

// 指针传递
func getPerson(ID uint) (*person, error) {
	return nil,errors.New("err")
}

// 值传递
func getPerson_second(ID uint) (person, error) {
	return person{}, errors.New("err")
}

纠结这个问题其实是因为在写接口的过程中发现,Go 里是没办法让一个实体类直接返回 nil ,只能返回默认构造参数的结构体,不然 ide 会报错,而使用结构体的指针可以返回 nil 。我虽然明白可以通过 err 返回值来处理错误,但是总觉得生成一个和非空的类外表一样的类返回给上层,会有潜在的危险,并且当类比较复杂的时候,可能会产生额外的开销。

接下来这个延申问题同样很困扰我:像 Go 这样存在指针而且语法很像 C/C++系但同时又存在 GC 的语言,大量使用指针传递对 GC 来说是好还是坏?要是在 IO 密集的 Web 应用场景下,这样大量地使用指针在堆上分配空间会不会加重 GC 的负担,反而会比值传递这种栈复制来得更慢,而不是让程序变得更快?

3377 次点击
所在节点    程序员
23 条回复
wheeler
2023-01-10 00:57:52 +08:00
go 里面惯例是有 error 的话,就不应该假定返回值是可用的吧。

大量用指针确实会有你说的问题,但是这种情况还是需要 profile 的。

个人以为,通常从语意以及上下文选择更为合适。
CEBBCAT
2023-01-10 02:18:40 +08:00
(O) 并且当类比较复杂的时候,可能会产生额外的开销。

嘻嘻,Go 的好处就是便于 benchmark 和调试。你可以看看性能上到底有没有差异,又是差了几纳秒。

关于在有 error 情况下该返回 nil 还是零值的问题,我推荐你看看 Go 自己是怎么处理的,也可以参考一些开源代码。

我个人是返回零值,因为编译器优化、降低 GC 压力。另外你担心零值被使用是吧?那你再多想一步,如果返回指针,那每次用之前是不是还要判断是不是 nil ?何况有了 error ,解决 error 是当务之急。


你后面的问题好专业,真厉害,我建议你先 Google 一下。应该会有很多 Golang 性能优化的建议,比如 map value 用 plain 值,hot path 之类的。最好中英一起搜。
jorneyr
2023-01-10 09:04:31 +08:00
向上层传指针呢,还是传实体?

个人觉得相差不大,本来 Go 的运行速度也不快,加上 Go 的内存逃逸分析会自动处理一部分,所以无脑的传指针好了。
bruce0
2023-01-10 09:06:59 +08:00
1. 一般 error 不是 nii 了 就可以认为这个值是有问题了

2. 返回指针会导致结构体逃逸, 然后分配到堆上, 如果量大的话 会有 gc 压力

我一般情况下,如果结构体都是基本类型, 而且字段不多的情况下, 不返回指针, 直接用值拷贝, 没有 gc, 如果结构体的字段多, 会用指针了
seers
2023-01-10 09:18:35 +08:00
取决于 struct 的大小,太大传值会有拷贝开销,太小的传指针会有堆开销
securityCoding
2023-01-10 09:51:13 +08:00
鹅厂内部框架返回的是指针类似,比较灵活
hwdef
2023-01-10 09:59:29 +08:00
大量传指针确实有你说的问题,,可能有潜在危险,gc 负担大。。但传值会增大内存使用量,看怎么取舍了
Rooger
2023-01-10 10:01:47 +08:00
为了统一性,全部使用指针。其实也是符合了 KISS 原则。
对于你考虑到的 GC 问题,其实是大部分从 C/C++ 转过来的共同的困扰。但是我记得编译器在这块有一定的优化,建议先全部使用指针吧,确定发现有性能问题的时候,再考虑优化吧。前期用最统一,最简单的代码把功能实现出来。
x1aoYao
2023-01-10 10:07:17 +08:00
既没有 Option<T> Result<T, E> 又没有 Exception 就是这样...
我司一般用的返回指针,比如从数据库里查询实体,GC 消耗有就有吧
对于判断了 error 是否还要判断 nil 的问题,有些默认的非空,有些用是否空表示数据是否存在(把无数据 error 返回 nil, nil 了, 避免 if err != nil { return err} 直接返回了,不然还要判断 err 的类型或值..
xylophone21
2023-01-10 10:19:01 +08:00
外面传一个指针进来,在函数里面赋值. 内存逃逸问题,空对象问题,都解决了. C/C++里都是这么干的, 用起来会烦一些, 性能提升可能也不多, 看你的场景到底在不在乎, 以及这个代码你打算用多久.
ShayneWang
2023-01-10 10:19:24 +08:00
遇事不决用指针
maigebaoer
2023-01-10 10:25:55 +08:00
@ShayneWang 确实如此,Go 很狗😃
fioncat
2023-01-10 10:29:11 +08:00
可以期待一下 go1.20 的 arena ,你可以自己决定对象什么时候释放。

https://github.com/golang/go/issues/51317
dobelee
2023-01-10 10:34:31 +08:00
日常业务开发直接指针。统一规范。如果大的用指针小的用结构体太鸡肋了,维护迭代也是要额外成本的。
maigebaoer
2023-01-10 10:38:53 +08:00
@xylophone21 标准库的 read
bli22ard
2023-01-10 10:46:31 +08:00
除非你确实需要给调用者返回一个副本,否则就应该使用指针
allgy
2023-01-10 10:48:15 +08:00
别纠结,这个问题不应该是你继续往前走的绊脚石
aiqinxuancai
2023-01-10 11:02:58 +08:00
每当接触新语言的时候,总是会遇到,实现这个功能最好的写法最正确的写法是什么的疑问。
我的总结下来就是,暂时不要过于纠结这些,真的没有必要,一些东西是循环渐进的,这些没必要的担忧只会影响你学习的进度,先实现功能,等过一年,再来看看你的老代码,你应该就知道该怎么做了。
当年学 C\C++,就是最简单的查找文本,文本对比等等基础方法都一堆,你总不能写的时候先去把每一种都实现实现,再对比对比速度吧,循环几十万次,差个那几 ms ,没必要的。
summerLast
2023-01-10 11:37:24 +08:00
在 rust 中就不会有这个问题
777777
2023-01-10 13:12:39 +08:00
传值,我就不喜欢用指针,因为如果 error 忘了出来,整个程序 nil 空指针会 panic

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/907760

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX