Инициализировать получатель указателя в методе указателя 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("This person should be initialized. Why is that not the case?")
    }
    fmt.Println(person)
}

Можно ожидать, что человек будет инициализирован (обнулен) после вызова метода.Born(), который является получателем указателя. Но это не тот случай. Может ли кто-то пролить свет на это?

4 ответа

Можно ожидать, что человек будет инициализирован (обнулен) после вызова метода.Born(), который является получателем указателя.

Вызов метода для получателя предполагает, что получатель уже инициализирован.

Так что вам нужно его инициализировать:

var person *Person
person = &Person{}  // Sets the pointer to point to an empty Person{} struct

Или в одном утверждении:

var person = &Person{}

Или стенография:

person := &Person{}

Причина вашей предполагаемой самоинициализации не удалась:

func (p *Person) Born() {
    if nil == p {
        p = new(Person)
    }
}

Это ваше новое назначение p относится к Born() функция, поэтому вне функции это никак не влияет.

Очевидно, что person не будет инициализирован методом Born, Параметры передаются по значению через присвоение параметров параметрам.

Спецификация языка программирования Go

Тип метода - это тип функции с получателем в качестве первого аргумента.

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
}

// 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)
    }

}

Выход:

<nil>
<nil>
<nil>
<nil>

Я думаю, что вместо этого вам нужна функция "конструктор" или "фабрика":

type Person struct {
    name string
    age  int
}

func NewPerson(name string) *Person {
    return &Person{
        name: name,
    }
}

person := NewPerson("John Doe")

Как правило, рекомендуется попытаться определить ваши типы таким образом, чтобы их так называемое "нулевое значение" - значение, которое получает переменная этого типа, если она не инициализирована явно, - готово для использования сразу же. В вашем случае сомнительно, что нулевое значение для Person разумно, потому что это будет иметь age 0, что вполне разумно, и name быть пустой строкой, которая может быть или не быть в порядке.

NewPerson Функция может инициализироваться как человек, не используя структуру Person и метод Born, чтобы получить нового 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)
}
Другие вопросы по тегам