Инициализировать получатель указателя в методе указателя 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)
}