如果S包含一个匿名字段T,S的方法集是否包括带有接收器*T的提升方法?

huangapple go评论144阅读模式
英文:

If S contains an anonymous field T, does the method sets of S include promoted methods with receiver *T?

问题

问题的标题几乎是从golang规范中引用的:

给定一个结构体类型S和一个名为T的类型,被提升的方法将包含在结构体的方法集中,具体规则如下:

  • 如果S包含一个匿名字段T,则S和*S的方法集都包括具有接收器T的被提升的方法。S的方法集还包括具有接收器T的被提升的方法。

这是一个Go Playground,展示了方法inc()是被提升的。

package main

import (
	"fmt"
)

// 只是一个int的包装器
type integer struct {
	i int
}

func (self *integer) inc() {
	self.i++
}

type counter struct {
	integer
}

func main() {
	c := counter{}

	c.inc()
	fmt.Println(c)
}
英文:

The title of the question is almost quoted from the golang specification:

> Given a struct type S and a type named T, promoted methods are
> included in the method set of the struct as follows:
>
> - If S contains an anonymous field T, the method sets of S and *S both include promoted methods with receiver T. The method set of *S also includes promoted methods with receiver *T.

This is a go playground shows that The method inc() is promoted.

package main

import (
	"fmt"
)

// just an int wrapper
type integer struct {
	i int
}

func (self *integer) inc() {
	self.i++
}

type counter struct {
	integer
}

func main() {
	c := counter{}

	c.inc()
	fmt.Println(c)
}

答案1

得分: 8

*T的方法不会被提升。规范没有明确允许这样做,所以是不允许的。然而,这背后有一个原因。

有时候你可能会在T上调用*T的方法。然而,这会隐式地引用一个指针。*T的方法不被认为是T的方法集的一部分。

根据Go规范中的调用部分

> 如果x是可寻址的,并且&x的方法集包含m,那么x.m()是(&x).m()的简写形式。

根据Go规范中的取地址操作符部分

> 对于类型为T的操作数x,取地址操作&x会生成一个类型为*T的指针,指向x。操作数必须是可寻址的,也就是说,要么是一个变量、指针间接引用或切片索引操作;要么是可寻址结构体操作数的字段选择器;要么是可寻址数组的数组索引操作。作为对可寻址要求的例外,x也可以是一个(可能带括号的)复合字面量。

如果S包含T,你甚至不需要取它的地址,所以可以调用这些方法。如果S包含T,你知道T是可寻址的,因为T是一个指针间接引用结构体的字段选择器。对于包含T的S,这是无法保证的。


更新:为什么这段代码能够工作?

记住S包含了T的方法集。还有,正如我之前引用的:

> 如果x是可寻址的,并且&x的方法集包含m,那么x.m()是(&x).m()的简写形式。

将这两者结合起来,你就有了答案。counter是可寻址的,&counter包含了*T的方法集。因此,counter.Inc()是(&counter).Inc()的简写形式。

英文:

No the methods of *T would not be promoted. The specification doesn't explicitly allow it so it isn't allowed. However, there is a reason behind this.

At times you may call a *T method on T. However, there is an implicit reference taken. Methods of *T are not considered part of T's method set.

From the calls section of the Go specification:

> If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

From the address operator section of the Go specification:

> For an operand x of type T, the address operation &x generates a pointer of type *T to x. The operand must be addressable, that is, either a variable, pointer indirection, or slice indexing operation; or a field selector of an addressable struct operand; or an array indexing operation of an addressable array. As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal.

If a S contains a *T, you don't even need to take its address so the methods can be called. If *S contains a T, you know T is addressable because T is a field selector of a pointer indirected struct. For S containing T, this cannot be guaranteed.


UPDATE: why does that code work?

Remember that *S contains the method set of *T. Also, as I quoted before:

> If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

Put the two together and you have your answer. Counter is addressable and &counter contains the method set of *T. Therefore, counter.Inc() is shorthand for (&counter).Inc().

huangapple
  • 本文由 发表于 2013年12月4日 02:25:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/20358801.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定