Golang中指针和值类型的区别与应用

Golang中指针和值类型的区别与应用 对于以下代码:

type Checkable interface {
      check() bool
  }

var _ Checkable = (*A)(nil)

type A struct {
}

func (a A) check() bool {
   return true
}

我原本期望 var _ Checkable = (*A)(nil) 会失败,因为是 A 实现了 check 方法,而不是 *A,但它却编译通过了。https://play.golang.org/p/s3nYALUyY7D


更多关于Golang中指针和值类型的区别与应用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

7 回复

感谢您指出了空格问题!我刚刚更新了问题。

更多关于Golang中指针和值类型的区别与应用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


非常感谢您分享对此的看法!我想我明白您的意思了。我认为一个类似的比较是:Go语言可以隐式解引用指针类型,但不会自动“引用”值类型。

就满足 Checkable 接口而言,它们是相同的。

嗯,我不确定它们是否相同。如果我使用

func (a A) check() bool {
   return true
}

var _ Checkable = &A{} 会失败,并提示 A does not implement Checkable (check method has pointer receiver)

https://play.golang.org/p/y5ETER4E5Uj 构建失败。

然而,在内联代码中,下划线和类型之间没有空格,这极大地改变了语义并导致构建失败:https://play.golang.org/p/-OSVVEylIMT

func (a A) check() bool {
   return true
}

func (a *A) check() bool {
   return true
}

在满足 Checkable 接口方面是相同的。

区别在于函数接收器的类型。当 check 需要你修改 A 时,你应该选择第二种。

是的,你说得对。我之前只从接口可满足性的角度回答了你的问题,就像这个示例中展示的那样,但我现在意识到存在一个2x2的情况矩阵。

但回到你最初的例子:我最好的猜测是它能工作,是因为你总是可以从指针获取值接收器(即使在运行时可能会引发panic)。当值在栈中通过函数调用传递时会被复制(从而获得不同的指针),在这种情况下,从值结构体获取指针接收器是没有意义的。

不过,我很想听听其他人怎么说。好问题!

在Go语言中,当值类型(A)实现了接口方法时,其指针类型(*A)也会自动满足该接口。这是因为Go编译器会自动生成指针接收器的方法集,使其包含值接收器的方法。具体来说:

  1. 方法集规则

    • 值类型 T 的方法集包含所有值接收器声明的方法。
    • 指针类型 *T 的方法集包含所有值接收器和指针接收器声明的方法。
  2. 接口实现

    • 如果值类型 A 实现了接口 Checkable 的方法 check(),那么 *A 也隐式实现了该接口,因为 *A 的方法集包含了 A 的方法。
  3. 代码示例验证: 在你的代码中,A 通过值接收器实现了 check() 方法,因此 *A 也自动拥有该方法,使得 var _ Checkable = (*A)(nil) 编译通过。

示例代码:

package main

import "fmt"

type Checkable interface {
    check() bool
}

type A struct{}

func (a A) check() bool {
    return true
}

func main() {
    var a A
    var pa *A = &a

    // 值类型和指针类型均可调用 check 方法
    fmt.Println(a.check())   // true
    fmt.Println(pa.check())  // true

    // 接口赋值验证
    var c1 Checkable = a   // 成功:A 实现了 Checkable
    var c2 Checkable = pa  // 成功:*A 也实现了 Checkable
    fmt.Println(c1.check(), c2.check()) // true true
}
  1. 反向情况不成立: 如果方法使用指针接收器(例如 func (a *A) check() bool),则值类型 A 不会自动实现接口,因为值类型的方法集不包含指针接收器的方法。

示例:

type B struct{}

func (b *B) check() bool {
    return true
}

func main() {
    var b B
    var pb *B = &b

    // 仅指针类型可调用 check 方法
    // b.check()  // 编译错误:不能通过值类型调用指针接收器方法
    fmt.Println(pb.check()) // true

    // 接口赋值
    // var c1 Checkable = b   // 编译错误:B 未实现 Checkable
    var c2 Checkable = pb  // 成功:*B 实现了 Checkable
}

总结:你的代码编译通过是因为Go语言的方法集规则允许指针类型 *A 继承值类型 A 的方法实现,从而满足接口要求。

回到顶部