为了账号安全,请及时绑定邮箱和手机立即绑定

实现 Mixin 和编译器行为的不一致

实现 Mixin 和编译器行为的不一致

Go
aluckdog 2021-09-13 15:15:22
Mixins 可以使用嵌入在 Go (1.4.1) 中实现,并且由于不struct{}占用内存(据我所知)它适合我们想要添加一些功能或只是向一个实际上无关的类型添加方法的情况与它的状态,但我们喜欢避免ParseThing(...),而是写thing.Parse(...).所以有:type X struct{}func (x X) F() {    fmt.Println("functionality in X.F()")}type Y struct{ X }type Z struct{ Y }那么如果我们这样做:var z Zz.F()会给我们:functionality in X.F()到现在为止还挺好。现在让我们OX使用方法添加另一种类型F()并将其嵌入Z:type Z struct {    Y    OX}type OX struct{} // overriding Xfunc (x OX) F() {    fmt.Println("functionality in OX.F()")}有趣的!现在我们得到了functionality in OX.F(),它向我们展示了 Go 编译器搜索该方法,从 type it self 开始,然后是最后一个嵌入类型。我们可以通过添加F()到Z:func (x Z) F() {    fmt.Println("functionality in Z.F()")}输出是functionality in Z.F()。现在,如果我们删除该Z.F()方法并添加F()到Y://func (x Z) F() {//    fmt.Println("functionality in Z.F()")//}func (x Y) F() {    fmt.Println("functionality in Y.F()")}然后我们看到这个错误ambiguous selector z.F;通过指针重定向没有区别。问题1:为什么会这样?额外的间接级别Y意味着其他东西,但让我想到了这一点。正如我所猜测的那样,这func (t T) String() string{}是一个例外。这段代码:type X struct{}func (x X) String() string {    return "in X.String()"}type Y struct{ X }type Z struct {    Y    OX}type OX struct{} // overriding Xfunc (x OX) String() string {    return "in OX.String()"}func (x Y) String() string {    return "in Y.String()"}然后这个:var z Zfmt.Println(z)给我们:{in Y.String() in OX.String()}这是合乎逻辑的。但是如果我们使用指针接收器:import (    "fmt"    "testing")func TestIt(t *testing.T) {    var z Z    fmt.Println(z)}type X struct{}func (x *X) String() string {    return "in X.String()"}type Y struct{ X }type Z struct {    Y    OX}type OX struct{} // overriding Xfunc (x *OX) String() string {    return "in OX.String()"}func (x *Y) String() string {    return "in Y.String()"}会打印出来:{{{}} {}}问题2:为什么会这样?
查看完整描述

2 回答

?
GCT1015

TA贡献1827条经验 获得超4个赞

问题 1

编译器是正确的。它应该如何决定,其中OX.FY.F应该使用它?它不能。因此,直接调用所需的方法取决于您:要么使用

z.Y.F()

或者

z.OX.F()

编辑:至于为什么你的榜样的工作,直到你定义FY,这是中提到的规格

对于 x 类型T*TwhereT不是指针或接口类型的值,x.f表示最浅深度的字段或方法,T其中存在此类f如果没有f最浅的深度,则选择器表达式是非法的。

(加了重点。)

在定义方法之前,最浅的实现是OX.F. 在你定义之后Y.FF在同一层上变成了两个s,这是非法的。

问题2

同样,编译器是正确的。您已将类型Y和嵌入OXZ,而不是*Y*OX。正如规范中所写,

对应指针类型*T的方法集是所有用receiver *Tor声明T的方法的集合(也就是包含了的方法集T)。

*T具有 的所有方法T,但不是相反。方法套OXY是空的,所以很明显,fmt.Println只是打印他们,如果他们是任何其它种类的结构没有String()定义的方法。


查看完整回答
反对 回复 2021-09-13
?
SMILET

TA贡献1796条经验 获得超4个赞

Ainar-G 写出简洁的答案

规格:

对于 T 或 *T 类型的值 x,其中 T 不是指针或接口类型,xf 表示 T 中最浅深度处存在此类 f 的字段或方法。如果没有一个 f 的深度最浅,则选择器表达式是非法的。

我想补充一点

规格:

如果 S 包含匿名字段 T,则 S 和 *S 的方法集都包含接收者 T 的提升方法。 *S 的方法集还包括接收者 *T 的提升方法。

因此,如果您仅使用引用来推广诸如

fmt.Println(&z)

但这会导致选择的歧义,因为 String 方法的可能性很小,因此由于规范,选择器 String 是非法的。编译器必须抱怨,但它没有。这种行为看起来没有具体说明,只能将其解释为我认为的常见打印操作的特例。这将按预期工作

var y Y
fmt.Println(&y)

这是工作示例 游乐场


查看完整回答
反对 回复 2021-09-13
  • 2 回答
  • 0 关注
  • 189 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信