英文:
Initialize pointer receiver in pointer method Go
问题
如何使用指针方法初始化指针接收器?
package main
import "fmt"
type Person struct {
name string
age int
}
func (p *Person) Born() {
if nil == p {
p = new(Person)
}
}
func main() {
var person *Person
person.Born()
if person == nil {
fmt.Println("这个人应该被初始化。为什么不是这样?")
}
fmt.Println(person)
}
在调用.Born()方法(指针接收器)之后,人们期望person被初始化(清零)。但事实并非如此。有人能解释一下吗?
英文:
How can I initialize a pointer receiver with a pointer method?
package main
import "fmt"
type Person struct {
name string
age int
}
func (p *Person) Born() {
if nil == p {
p = new(Person)
}
}
func main() {
var person *Person
person.Born()
if person == nil {
fmt.Println("This person should be initialized. Why is that not the case?")
}
fmt.Println(person)
}
One would expect person to be initialized (zeroed) after calling .Born() method which is a pointer receiver. But that is not the case. Could someone shed some light on this?
答案1
得分: 17
在调用.Born()方法之后,人们会期望person被初始化(清零),而.Born()方法是一个指针接收器。
调用接收器上的方法假设接收器已经被初始化。
所以你需要初始化它:
var person *Person
person = &Person{} // 将指针指向一个空的Person{}结构体
或者可以在一条语句中完成:
var person = &Person{}
或者使用简写形式:
person := &Person{}
你的预期的自我初始化失败的原因是:
func (p *Person) Born() {
if nil == p {
p = new(Person)
}
}
是因为你对p
的新赋值仅在Born()
函数的作用域内有效,所以在函数外部它没有任何效果。
英文:
> One would expect person to be initialized (zeroed) after calling .Born() method which is a pointer receiver.
Calling a method on a receiver assumes that the receiver is already initialized.
So you need to initialize it:
var person *Person
person = &Person{} // Sets the pointer to point to an empty Person{} struct
Or in a single statement:
var person = &Person{}
Or shorthand:
person := &Person{}
The reason your intended self-initialization is failing:
func (p *Person) Born() {
if nil == p {
p = new(Person)
}
}
Is that your new assignment to p
is scoped to the Born()
function, so outside the function it has no effect.
答案2
得分: 2
显然,Born
方法不会初始化 person
。参数通过将参数赋值给参数来传递值。
方法的类型是将接收器作为第一个参数的函数的类型。
type Point struct{ x, y float64 } func (p *Point) Scale(factor float64) { p.x *= factor p.y *= factor }
例如,方法
Scale
的类型是func(p *Point, factor float64)
然而,以这种方式声明的函数不是方法。
在函数调用中,函数值和参数按照通常的顺序进行求值。在它们被求值之后,调用的函数的参数通过值传递给函数,并且被调用的函数开始执行。当函数返回时,返回参数通过值传递回调用函数。
为了说明,这里有各种形式的 Born
方法调用。p
的作用域仅限于方法或函数调用。
package main
import "fmt"
type Person struct {
name string
age int
}
// 方法
func (p *Person) Born() {
if nil == p {
p = new(Person)
}
}
// 方法作为函数
func Born(p *Person) {
if nil == p {
p = new(Person)
}
}
func main() {
// 初始方法调用形式
{
var person *Person
person.Born()
fmt.Println(person)
}
// 等效的按值调用形式
{
var person *Person
{
p := person
p.Born()
}
fmt.Println(person)
}
// 等效的方法调用作为函数调用形式
{
var person *Person
{
p := person
Born(p)
}
fmt.Println(person)
}
// 等效的方法调用作为内联函数形式
{
var person *Person
{
p := person
if nil == p {
p = new(Person)
}
}
fmt.Println(person)
}
}
输出:
<nil>
<nil>
<nil>
<nil>
英文:
Clearly, person
will not be initialized by method Born
. Parameters are passed by value through the assignment of arguments to parameters.
> The Go Programming Language
> Specification
>
> The type of a method is the type of a function with the receiver as
> first argument.
>
> type Point struct{ x, y float64 }
>
> func (p *Point) Scale(factor float64) {
> p.x *= factor
> p.y *= factor
> }
>
> For instance, the method Scale
has type
>
> func(p *Point, factor float64)
>
> However, a function declared this way is not a method.
>
> In a function call, the function value and arguments are evaluated in
> the usual order. After they are evaluated, the parameters of the call
> are passed by value to the function and the called function begins
> execution. The return parameters of the function are passed by value
> back to the calling function when the function returns.
To illustrate, here are various forms of the Born
method call. The scope of p
is limited to the method or function call.
package main
import "fmt"
type Person struct {
name string
age int
}
// Method
func (p *Person) Born() {
if nil == p {
p = new(Person)
}
}
// Method as function
func Born(p *Person) {
if nil == p {
p = new(Person)
}
}
func main() {
// Initial method call form
{
var person *Person
person.Born()
fmt.Println(person)
}
// Equivalent method call by value form
{
var person *Person
{
p := person
p.Born()
}
fmt.Println(person)
}
// Equivalent method call as function call form
{
var person *Person
{
p := person
Born(p)
}
fmt.Println(person)
}
// Equivalent method call as inline function form
{
var person *Person
{
p := person
if nil == p {
p = new(Person)
}
}
fmt.Println(person)
}
}
Output:
<nil>
<nil>
<nil>
<nil>
答案3
得分: 1
我认为你需要的是"构造函数"或"工厂"函数:
type Person struct {
name string
age int
}
func NewPerson(name string) *Person {
return &Person{
name: name,
}
}
person := NewPerson("John Doe")
通常建议尝试以这样的方式定义类型,使得它们的所谓"零值"——即变量在没有显式初始化的情况下获得的值——可以立即使用。
在你的情况下,对于Person
类型的零值是否合理是有问题的,因为它将具有age
为0,这是完全合理的,而name
为空字符串,这可能是可以接受的,也可能不可以。
英文:
I think what you need instead is "constructor" or "factory" function:
type Person struct {
name string
age int
}
func NewPerson(name string) *Person {
return &Person{
name: name,
}
}
person := NewPerson("John Doe")
Generally, it's advised to try to define your types in such a way
so that their so-called "zero value"—the value a variable of this
type gets when it's not explicitly initialized otherwise—is ready
to be used right away.
In your case, it's questionable whether the zero value for Person
is
sensible because it will have age
of 0, which is perfectly reasonable,
and name
being an empty string, which may or may not be OK.
答案4
得分: 0
NewPerson
函数可以初始化一个人,而不是使用Person
结构体和Born
方法来获取一个新的人。
package main
import (
"fmt"
"time"
)
type Person struct {
Name string
Dob time.Time
}
func NewPerson(name string, year, month, day int) *Person {
return &Person{
Name: name,
Dob: time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local),
}
}
func (p *Person) GetAge() string {
d := time.Since(p.Dob)
return fmt.Sprintf("%s's age is %d", p.Name, int(d.Hours()/24/365))
}
func (p *Person) Born() {
p.Name = "New born (unnamed)"
p.Dob = time.Now()
}
func main() {
joe := NewPerson("Joe", 1999, 12, 31)
joeAge := joe.GetAge()
fmt.Println(joeAge)
newPerson := &Person{}
newPerson.Born()
newPersonAge := newPerson.GetAge()
fmt.Println(newPersonAge)
}
请注意,这是一个Go语言的代码示例,用于演示如何使用NewPerson
函数初始化一个人,并使用GetAge
方法获取年龄。Born
方法用于将人的姓名设置为"New born (unnamed)",并将出生日期设置为当前时间。在main
函数中,我们创建了一个名为"Joe"的人,并打印出他的年龄。然后,我们创建了一个新的人,并打印出他的年龄。
英文:
NewPerson
function can initialize as a person, nor using Person struct and Born method to get a new Person.
package main
import (
"fmt"
"time"
)
type Person struct {
Name string
Dob time.Time
}
func NewPerson(name string, year, month, day int) *Person {
return &Person{
Name: name,
Dob: time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.Local),
}
}
func (p *Person) GetAge() string {
d := time.Since(p.Dob)
return fmt.Sprintf("%s's age is %d", p.Name, int(d.Hours()/24/365))
}
func (p *Person) Born() {
p.Name = "New born (unnamed)"
p.Dob = time.Now()
}
func main() {
joe := NewPerson("Joe", 1999, 12, 31)
joeAge := joe.GetAge()
fmt.Println(joeAge)
newPerson := &Person{}
newPerson.Born()
newPersonAge := newPerson.GetAge()
fmt.Println(newPersonAge)
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论