V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
winRain
V2EX  ›  Go 编程语言

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

  •  
  •   winRain · 2022-07-23 21:57:05 +08:00 · 3128 次点击
    这是一个创建于 852 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

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

    还是建议都来 C++,自己封装个 interface 类,什么引用、指针、右值都能分别捕获,不要太爽。
    josexy
        7
    josexy  
       2022-07-24 08:48:49 +08:00   ❤️ 2
    blless
        8
    blless  
       2022-07-24 12:57:21 +08:00 via Android
    你的例子里面 simpleApi 没有私有变量,加一个比如 id 然后 say 的例子用 id 加 name ,然后调用过程修改 id 试试就知道区别了
    tramm
        9
    tramm  
       2022-07-24 14:01:20 +08:00
    因为一是 `*SimpleApi` 实现的 `API`, 二是`SimpleApi`实现的`API`. 两者不一样, 因为返回值是`API`, 所以哪个实现了`API`才能返回.
    winRain
        10
    winRain  
    OP
       2022-07-24 14:08:40 +08:00
    @Hstar 看了大佬的解释一下就明白了
    winRain
        11
    winRain  
    OP
       2022-07-24 14:15:15 +08:00
    @josephxrays 感谢大佬找的两篇文章,明白原因了
    CEBBCAT
        12
    CEBBCAT  
       2022-07-24 14:37:35 +08:00
    securityCoding
        13
    securityCoding  
       2022-07-24 15:03:38 +08:00
    指针,指针,指针
    aladdinding
        14
    aladdinding  
       2022-07-25 14:48:19 +08:00
    @kidlj https://go.dev/doc/effective_go#pointers_vs_values 看文档应该是在编译阶段吧 不是调用阶段
    777777
        15
    777777  
       2022-07-27 13:50:44 +08:00
    建议用 2 ,能不用指针就别用,否则空指针 panic 就难受了。只有传大结构体时才用指针。多人都会有种误解,认为传 struct 的指针比复制一份值快很多,所以喜欢传 struct 的指针。但实际上并不是的,指针引用的对象是分配到堆上的,在函数内使用指针引用的值都需要取堆去取,并且堆中的内存受 GC 管理会增加 GC 压力。而传值的话复制后的值会直接分配在栈上,栈的速度比堆快,并且函数执行完毕后栈会销毁没有 GC 之类的压力。
    tairan2006
        16
    tairan2006  
       2022-08-04 09:45:02 +08:00
    @777777 其实这个 best practice 有点迷,就是“多大的 struct 算大”,一般成员变量不超过 5 个我会直接复制,但是超过 5 个用指针是否合适我也不知道啊…
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5865 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 02:11 · PVG 10:11 · LAX 18:11 · JFK 21:11
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.