楼主这几天心血来潮,看了下 golang 语法(本身是 java 出身),发现一个让我疑惑的问题:
代码如下
写法一:
package factory
type Api interface {
Say(name string) string
}
type SimpleApi struct {
}
func (*SimpleApi) Say(name string) string {
return "this is " + name
}
func CreateApi(t int) Api {
if t == 1 {
// 只能这样写
return &SimpleApi{}
}
return nil
}
写法二:
package factory
type Api interface {
Say(name string) string
}
type SimpleApi struct {
}
func (SimpleApi) Say(name string) string {
return "this is " + name
}
func CreateApi(t int) Api {
if t == 1 {
// 两种写法皆可
//return &SimpleApi{}
return SimpleApi{}
}
return nil
}
不能理解的是,为什么写法一不能写成 return SimpleApi{}
。
golang 不是可以隐式的进行取地址和解引用操作吗? 基于 golang 1.18
1
Mitt 2022-07-23 22:52:19 +08:00
因为写法二是拷贝,取和解是一样的,写法一则不是,每次取的都不是预期结果
|
2
kidlj 2022-07-23 23:09:22 +08:00
变量自动取地址和指针自动解引用是在方法的*调用*阶段,为了方便所以加了这样的语法糖。
而案例中 return 值到 interface 变量,是在方法的*定义*阶段,可能这时候不让具体类型变量满足指针 receiver 的 interface ,为了强调吧。当然假如不是这种设计,像 op 期待的那样,返回值是具体类型,自动取地址以满足指针 recieve 的 interface ,实现上应该也是可以的,只不过设计者没有做这种选择。我个人还是觉得就是为了在方法的*定义*阶段强调返回值的正确性吧。 Anyway ,暴露指针而不是采用引用类型,可能是 Go 的缺点之一吧。(如果采用引用类型又可能会带来什么问题,我没有了解)。 |
3
Hstar 2022-07-23 23:23:26 +08:00 3
根本原因是 go 中参数传递都是值传递,编译器为了不出错做了这个兼容。用 java 的话说,写法一中的 Say 是个实例方法,所以必须传回实例,写法二中的方法是个类方法,你传回实例或者类都行。
用 go 的逻辑说,写法一中如果可以传回结构体,那么函数调用方拿到的是该返回结构体的拷贝,并不能修改和访问真正在 CreateApi 中创建的那个 SimpleApi 结构体,但是定义的 Say 方法是个指针方法,代表它应该可以访问和修改内部那个结构体,所以冲突了。 |
4
golangLover 2022-07-23 23:42:44 +08:00 via Android
@kidlj go 这种语言设计都是做一半不做一半,也不是一天两天的事了
|
5
zhuweiyou 2022-07-24 07:57:09 +08:00
返回 SimpleApi 是拷贝,返回&SimpleApi 是指针.
|
6
yulon 2022-07-24 08:19:59 +08:00
Javaer 不应该用 new 吗,Go 是可以用 new 的。
还是建议都来 C++,自己封装个 interface 类,什么引用、指针、右值都能分别捕获,不要太爽。 |
7
josexy 2022-07-24 08:48:49 +08:00 2
|
8
blless 2022-07-24 12:57:21 +08:00 via Android
你的例子里面 simpleApi 没有私有变量,加一个比如 id 然后 say 的例子用 id 加 name ,然后调用过程修改 id 试试就知道区别了
|
9
tramm 2022-07-24 14:01:20 +08:00
因为一是 `*SimpleApi` 实现的 `API`, 二是`SimpleApi`实现的`API`. 两者不一样, 因为返回值是`API`, 所以哪个实现了`API`才能返回.
|
11
winRain OP @josephxrays 感谢大佬找的两篇文章,明白原因了
|
12
CEBBCAT 2022-07-24 14:37:35 +08:00
想提一嘴「 Accept Interfaces Return Structs 」 https://bryanftan.medium.com/accept-interfaces-return-structs-in-go-d4cab29a301b
|
13
securityCoding 2022-07-24 15:03:38 +08:00
指针,指针,指针
|
14
aladdinding 2022-07-25 14:48:19 +08:00
@kidlj https://go.dev/doc/effective_go#pointers_vs_values 看文档应该是在编译阶段吧 不是调用阶段
|
15
777777 2022-07-27 13:50:44 +08:00
建议用 2 ,能不用指针就别用,否则空指针 panic 就难受了。只有传大结构体时才用指针。多人都会有种误解,认为传 struct 的指针比复制一份值快很多,所以喜欢传 struct 的指针。但实际上并不是的,指针引用的对象是分配到堆上的,在函数内使用指针引用的值都需要取堆去取,并且堆中的内存受 GC 管理会增加 GC 压力。而传值的话复制后的值会直接分配在栈上,栈的速度比堆快,并且函数执行完毕后栈会销毁没有 GC 之类的压力。
|
16
tairan2006 2022-08-04 09:45:02 +08:00
@777777 其实这个 best practice 有点迷,就是“多大的 struct 算大”,一般成员变量不超过 5 个我会直接复制,但是超过 5 个用指针是否合适我也不知道啊…
|