Reputation: 131
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?
Upvotes: 13
Views: 15342
Reputation: 6311
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)
}
Upvotes: 0
Reputation: 166588
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 typefunc(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>
Upvotes: 2
Reputation: 55463
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.
Upvotes: 1
Reputation: 79594
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.
Upvotes: 20