golang 用 & 返回对象和直接返回对象有啥区别?

2022-07-23 21:57:05 +08:00
 winRain

楼主这几天心血来潮,看了下 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

3167 次点击
所在节点    Go 编程语言
16 条回复
Mitt
2022-07-23 22:52:19 +08:00
因为写法二是拷贝,取和解是一样的,写法一则不是,每次取的都不是预期结果
kidlj
2022-07-23 23:09:22 +08:00
变量自动取地址和指针自动解引用是在方法的*调用*阶段,为了方便所以加了这样的语法糖。
而案例中 return 值到 interface 变量,是在方法的*定义*阶段,可能这时候不让具体类型变量满足指针 receiver 的 interface ,为了强调吧。当然假如不是这种设计,像 op 期待的那样,返回值是具体类型,自动取地址以满足指针 recieve 的 interface ,实现上应该也是可以的,只不过设计者没有做这种选择。我个人还是觉得就是为了在方法的*定义*阶段强调返回值的正确性吧。

Anyway ,暴露指针而不是采用引用类型,可能是 Go 的缺点之一吧。(如果采用引用类型又可能会带来什么问题,我没有了解)。
Hstar
2022-07-23 23:23:26 +08:00
根本原因是 go 中参数传递都是值传递,编译器为了不出错做了这个兼容。用 java 的话说,写法一中的 Say 是个实例方法,所以必须传回实例,写法二中的方法是个类方法,你传回实例或者类都行。

用 go 的逻辑说,写法一中如果可以传回结构体,那么函数调用方拿到的是该返回结构体的拷贝,并不能修改和访问真正在 CreateApi 中创建的那个 SimpleApi 结构体,但是定义的 Say 方法是个指针方法,代表它应该可以访问和修改内部那个结构体,所以冲突了。
golangLover
2022-07-23 23:42:44 +08:00
@kidlj go 这种语言设计都是做一半不做一半,也不是一天两天的事了
zhuweiyou
2022-07-24 07:57:09 +08:00
返回 SimpleApi 是拷贝,返回&SimpleApi 是指针.
yulon
2022-07-24 08:19:59 +08:00
Javaer 不应该用 new 吗,Go 是可以用 new 的。

还是建议都来 C++,自己封装个 interface 类,什么引用、指针、右值都能分别捕获,不要太爽。
josexy
2022-07-24 08:48:49 +08:00
blless
2022-07-24 12:57:21 +08:00
你的例子里面 simpleApi 没有私有变量,加一个比如 id 然后 say 的例子用 id 加 name ,然后调用过程修改 id 试试就知道区别了
tramm
2022-07-24 14:01:20 +08:00
因为一是 `*SimpleApi` 实现的 `API`, 二是`SimpleApi`实现的`API`. 两者不一样, 因为返回值是`API`, 所以哪个实现了`API`才能返回.
winRain
2022-07-24 14:08:40 +08:00
@Hstar 看了大佬的解释一下就明白了
winRain
2022-07-24 14:15:15 +08:00
@josephxrays 感谢大佬找的两篇文章,明白原因了
CEBBCAT
2022-07-24 14:37:35 +08:00
securityCoding
2022-07-24 15:03:38 +08:00
指针,指针,指针
aladdinding
2022-07-25 14:48:19 +08:00
@kidlj https://go.dev/doc/effective_go#pointers_vs_values 看文档应该是在编译阶段吧 不是调用阶段
777777
2022-07-27 13:50:44 +08:00
建议用 2 ,能不用指针就别用,否则空指针 panic 就难受了。只有传大结构体时才用指针。多人都会有种误解,认为传 struct 的指针比复制一份值快很多,所以喜欢传 struct 的指针。但实际上并不是的,指针引用的对象是分配到堆上的,在函数内使用指针引用的值都需要取堆去取,并且堆中的内存受 GC 管理会增加 GC 压力。而传值的话复制后的值会直接分配在栈上,栈的速度比堆快,并且函数执行完毕后栈会销毁没有 GC 之类的压力。
tairan2006
2022-08-04 09:45:02 +08:00
@777777 其实这个 best practice 有点迷,就是“多大的 struct 算大”,一般成员变量不超过 5 个我会直接复制,但是超过 5 个用指针是否合适我也不知道啊…

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

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

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

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

© 2021 V2EX