方法接收器作为指针和非指针之间的区别是什么?

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

Difference between a method receiver as pointer or not

问题

为什么我不需要将PrintValue()定义为指针接收器(*One),就能够打印出"hello"呢?

package main
import "fmt"


type One struct{
    a string
}
func (o *One)AssignValue(){
    o.a = "hello"
}
func (o One)PrintValue(){
    fmt.Println(o.a)
}

func main() {
    one := One{}
    one.AssignValue()
    one.PrintValue()
}

为了能够在PrintValue()函数中打印出"hello",我们不需要将其定义为指针接收器。因为在Go语言中,当我们将一个值类型的变量作为接收器时,Go会自动将其转换为对应的指针类型。所以在这个例子中,即使PrintValue()的接收器是值类型的One,我们仍然可以通过one.PrintValue()来打印出"hello"。

英文:

Why don't I have to define PrintValue() as a pointer receiver (*One) to be able to print "hello"?

package main
import "fmt"


type One struct{
    a string
}
func (o *One)AssignValue(){
    o.a = "hello"
}
func (o One)PrintValue(){
    fmt.Println(o.a)
}

func main() {
    one := One{}
    one.AssignValue()
    one.PrintValue()
}

答案1

得分: 0

因为one已经是类型One的实例。实例化语法

t := One{}

创建了一个类型为One,而形式

p := &One{}

创建了一个类型为One指针

这意味着当调用t.PrintValue时,不需要进行任何操作,因为接收器类型(One)已经与t的类型(也是One)相同。

当调用p.PrintValue时,编译器会自动将可寻址的变量转换为其指针形式,因为接收器类型(One)与p的类型(*One)不相等。因此,表达式

p.PrintValue()

被转换为

(*p).PrintValue()

当调用t.AssignValue时,也需要进行转换,因为该方法具有指针接收器,但我们提供的是一个值。在可能的情况下,编译器也会自动完成这个转换。

根据调用规范

> 如果方法集(x的类型)包含m,并且参数列表可以分配给m的参数列表,则方法调用x.m()是有效的。如果x是可寻址的并且&x的方法集包含m,则x.m()是&x.m()的简写形式

这意味着表达式

t.AssignValue()

被转换为

(&t).AssignValue()

请注意,这并不总是可能的。例如,在从函数返回值时:

func NewOne(s string) One { return One{s} }

NewOne("foo").AssignValue() // 不可能

x := NewOne("foo")
x.AssignValue() // 可能,自动转换
英文:

Because one is already of type One. The instantiation syntax

t := One{}

creates a value of type One while the form

p := &One{}

creates a pointer to a value of type One.

This means that nothing is to be done when calling t.PrintValue as the receiver type (One) is already the same as the type of t (One as well).

When calling p.PrintValue the compiler automatically converts an addressable variable to its pointer form because the receiver type (One) is not equal to the type of p (*One). So the expression

p.PrintValue()

is converted to

(*p).PrintValue()

There is also a conversion necessary when calling t.AssignValue as this method has a pointer receiver but we're supplying a value. This is also done automatically by the compiler where possible.

From the spec on calls:

> A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m. If x is addressable and &x's method set contains m, x.m() is shorthand for (&x).m()

This means the expression

t.AssignValue()

is converted to

(&t).AssignValue()

Note that this is not always possible. For example when returning a value from a function:

func NewOne(s string) One { return One{s} }

NewOne("foo").AssignValue() // Not possible

x := NewOne("foo")
x.AssignValue() // Possible, automatically converted

huangapple
  • 本文由 发表于 2014年1月11日 06:26:20
  • 转载请务必保留本文链接:https://go.coder-hub.com/21055609.html
匿名

发表评论

匿名网友

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

确定