英文:
Method Sets (Pointer vs Value Receiver)
问题
我很难理解为什么这些规则与指针类型和值类型的方法集相关联。
可以有人从接口表的角度解释一下原因吗?
(摘自William Kennedy的博客)
值类型 方法接收者
-----------------------------------------------
T (t T)
*T (t T) 和 (t *T)
方法接收者 值类型
-----------------------------------------------
(t T) T 和 *T
(t *T) *T
规范中的摘录
方法集
一个类型可以有一个与之关联的方法集。接口类型的方法集就是它的接口。任何其他类型T的方法集由所有声明了接收者类型为T的方法组成。相应指针类型T的方法集是所有声明了接收者类型为T或T的方法的集合(也就是说,它还包含了T的方法集)。对于包含匿名字段的结构体,还有其他规则,详见结构体类型的章节。其他任何类型都有一个空的方法集。在方法集中,每个方法必须有一个唯一的非空方法名。
类型的方法集确定了该类型实现的接口以及可以使用该类型的接收者调用的方法。
英文:
I am having a hard time understanding as to why are these rules associated with method set of pointer type .vs. value type
Can someone please explain the reason (from the interface table perspective)
(Snippet from William Kennedy's blog)
Values Methods Receivers
-----------------------------------------------
T (t T)
*T (t T) and (t *T)
Methods Receivers Values
-----------------------------------------------
(t T) T and *T
(t *T) *T
Snippet from specification
Method sets
A type may have a method set associated with it. The method set of an interface type is its interface.
The method set of any other type T consists of all methods declared with receiver type T. The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T). Further rules apply to structs containing anonymous fields, as described in the section on struct types. Any other type has an empty method set. In a method set, each method must have a unique non-blank method name.
The method set of a type determines the interfaces that the type implements and the methods that can be called using a receiver of that type.
答案1
得分: 49
- 如果你有一个
*T
,你可以调用接收者类型为*T
的方法,也可以调用接收者类型为T
的方法(你引用的段落,方法集)。 - 如果你有一个
T
并且它是可寻址的,你可以调用接收者类型为*T
的方法,也可以调用接收者类型为T
的方法,因为方法调用t.Meth()
等效于(&t).Meth()
(调用)。 - 如果你有一个
T
并且它不可寻址(例如,函数调用的结果,或者从映射中索引的结果),Go 无法获取指向它的指针,因此你只能调用接收者类型为T
而不是*T
的方法。 - 如果你有一个接口
I
,并且I
的方法集中的一些或全部方法由接收者为*T
的方法提供(其余方法由接收者为T
的方法提供),那么*T
满足接口I
,但T
不满足。这是因为*T
的方法集包括T
的方法集,但反过来不成立(回到第一点)。
简而言之,你可以混合使用值接收者和指针接收者的方法,并将它们与包含值和指针的变量一起使用,而不必担心它们的区别。两者都可以工作,语法相同。然而,如果需要使用指针接收者的方法来满足一个接口,那么只有指针才能赋值给该接口,值是无效的。
英文:
- If you have a
*T
you can call methods that have a receiver type of*T
as well as methods that have a receiver type ofT
(the passage you quoted, Method Sets). - If you have a
T
and it is addressable you can call methods that have a receiver type of*T
as well as methods that have a receiver type ofT
, because the method callt.Meth()
will be equivalent to(&t).Meth()
(Calls). - If you have a
T
and it isn't addressable (for instance, the result of a function call, or the result of indexing into a map), Go can't get a pointer to it, so you can only call methods that have a receiver type ofT
, not*T
. - If you have an interface
I
, and some or all of the methods inI
's method set are provided by methods with a receiver of*T
(with the remainder being provided by methods with a receiver ofT
), then*T
satisfies the interfaceI
, butT
doesn't. That is because*T
's method set includesT
's, but not the other way around (back to the first point again).
In short, you can mix and match methods with value receivers and methods with pointer receivers, and use them with variables containing values and pointers, without worrying about which is which. Both will work, and the syntax is the same. However, if methods with pointer receivers are needed to satisfy an interface, then only a pointer will be assignable to the interface — a value won't be valid.
答案2
得分: 7
从Golang FAQ中可以得知:
根据Go语言规范,类型T的方法集包含所有接收者类型为T的方法,而相应指针类型T的方法集包含所有接收者类型为T或T的方法。这意味着*T的方法集包含T的方法集,但反过来不成立。
这种区别的原因是,如果一个接口值包含指针*T,方法调用可以通过解引用指针来获取值,但如果一个接口值包含值T,方法调用无法安全地获取指针。(这样做将允许方法修改接口内部值的内容,这是语言规范不允许的。)
即使在编译器可以获取值的地址并将其传递给方法的情况下,如果方法修改了该值,更改将在调用者中丢失。例如,如果bytes.Buffer的Write方法使用值接收者而不是指针,那么以下代码:
var buf bytes.Buffer io.Copy(buf, os.Stdin)
将把标准输入复制到buf的副本中,而不是buf本身。这几乎永远不是期望的行为。
关于Golang接口的内部实现细节,可以参考以下链接:
英文:
From Golang FAQ:
> As the Go specification says, the method set of a type T consists of all methods with receiver type T, while that of the corresponding pointer type *T consists of all methods with receiver *T or T. That means the method set of *T includes that of T, but not the reverse.
>
> This distinction arises because if an interface value contains a pointer *T, a method call can obtain a value by dereferencing the pointer, but if an interface value contains a value T, there is no safe way for a method call to obtain a pointer. (Doing so would allow a method to modify the contents of the value inside the interface, which is not permitted by the language specification.)
>
> Even in cases where the compiler could take the address of a value to pass to the method, if the method modifies the value the changes will be lost in the caller. As an example, if the Write method of bytes.Buffer used a value receiver rather than a pointer, this code:
>go
>var buf bytes.Buffer
>io.Copy(buf, os.Stdin)
>
> would copy standard input into a copy of buf, not into buf itself. This is almost never the desired behavior.
About Golang interface under the hood.
答案3
得分: 0
- 在Go语言中,当我们有一个类型时,我们可以在其上附加方法,这些附加到类型上的方法被称为其方法集。
- 根据指针或非指针值的情况,它将确定附加到哪个方法。
案例1
接收器(t T) 值 T => https://go.dev/play/p/_agcEVFaySx
type square struct {
length int
}
type shape interface { // 将 shape 定义为接口
area() int
}
// 接收器(t T)
func (sq square) area() int {
return sq.length * sq.length
}
func describe(s shape) {
fmt.Println("area", s.area())
}
func main() {
sq := square{
length: 5,
}
describe(sq) // 值 `sq` (T)
}
案例2: 接收器(t T) 值 T
// 接收器(t *T)
func (sq *square) area() int {
return sq.length * sq.length
}
func main() {
describe(sq) // 值 sq (T)
}
案例3: 接收器(t *T) 值 T
// 接收器(t *T)
func (sq *square) area() int {
return sq.length * sq.length
}
func main() {
describe(&sq) // 值 sq (*T)
}
案例4: 接收器(t *T) 值 T
这个案例失败了
// 接收器(t *T)
func (sq *square) area() int {
return sq.length * sq.length
}
func main() {
describe(&sq) // 值 sq (T)
}
> 我们输入的是普通值而不是指针,但是方法接收器需要指针值,因此它不接受,失败了。
但是我们可以这样调用 area
方法 sq.area()
// 而不是使用接口来访问它。
英文:
-In go when we have a type we can attach methods on it, those methods attached to type are known as its method set.
- Depending on Pointer or not pointer value , it will determine which method attach to it.
Case:1
Receiver (t T) Value T => https://go.dev/play/p/_agcEVFaySx
type square struct {
length int
}
type shape interface { shape as an interface
area() int
}
// receiver(t T)
func (sq square) area() int {
return sq.length * sq.length
}
func describe(s shape) {
fmt.Println("area", s.area())
}
func main() {
sq := square{
length: 5,
}
describe(sq)// value `sq` (T)
}
Case 2: Receiver (t T) Value T
// receiver(t *T)
func (sq *square) area() int {
return sq.length * sq.length
}
func main() {
describe(sq)// value sq (T)
}
Case 4: Receiver (t *T) Value T
// receiver(t *T)
func (sq *square) area() int {
return sq.length * sq.length
}
func main() {
describe(&sq)// value sq (*T)
}
Case 4: Receiver (t *T) Value T
this case fails
// receiver(t *T)
func (sq *square) area() int {
return sq.length * sq.length
}
func main() {
describe(&sq)// value sq (T)
}
> we input normal value rather than pointer , but method receiver takes pointer value,it will not accept ,fails.
But we call area
method like this sq.area()
//rather than using interface to access it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论