atc0005 / learn

Various topics that I want to learn more about, either for professional development or for my own use
0 stars 0 forks source link

Go | Automatic (implicit) pointer dereferencing for pointer receivers #85

Open atc0005 opened 4 years ago

atc0005 commented 4 years ago

This is the example code from the https://www.udemy.com/course/go-the-complete-developers-guide/ course:

func (pointerToPerson *person) updateName(newFirstName string) {
    (*pointerToPerson).firstName = newFirstName
}

Here it looks like they're explicitly dereferencing the pointer receiver in order to get at the firstName field. I vaguely recall reading about Go automatically dereferencing for you with pointer receivers, so for the programmer's perspective this would be functionally equivalent (though less explicit perhaps?):

func (pointerToPerson *person) updateName(newFirstName string) {
    pointerToPerson.firstName = newFirstName
}

Need to do some additional research to confirm that my memory is correct and if possible, figure out what the best practice is for this.

atc0005 commented 4 years ago

From https://www.digitalocean.com/community/tutorials/defining-methods-in-go:

package main

import "fmt"

type Boat struct {
    Name string

    occupants []string
}

func (b *Boat) AddOccupant(name string) *Boat {
    b.occupants = append(b.occupants, name)
    return b
}

func (b Boat) Manifest() {
    fmt.Println("The", b.Name, "has the following occupants:")
    for _, n := range b.occupants {
        fmt.Println("\t", n)
    }
}

func main() {
    b := &Boat{
        Name: "S.S. DigitalOcean",
    }

    b.AddOccupant("Sammy the Shark")
    b.AddOccupant("Larry the Lobster")

    b.Manifest()
}

Snippet from description:

Within main, we define a new variable, b, which will hold a pointer to a Boat (*Boat). We invoke the AddOccupant method twice on this instance to add two passengers. The Manifest method is defined on the Boat value, because in its definition, the receiver is specified as (b Boat). In main, we are still able to call Manifest because Go is able to automatically dereference the pointer to obtain the Boat value. b.Manifest() here is equivalent to (*b).Manifest().

Worth stressing:

atc0005 commented 4 years ago

From The Go Programming Language:

The (*Point).ScaleBy method can be called by providing a *Point receiver, like this:

r := &Point{1, 2} 
r.ScaleBy(2) 
fmt.Println(*r) // "{2, 4}" 

or this:

p := Point{1, 2} 
pptr := &p 
pptr.ScaleBy(2) 
fmt.Println(p) // "{2, 4}" 

or this:

p := Point{1, 2} 
(&p).ScaleBy(2) 
fmt.Println(p) // "{2, 4}" 

But the last two cases are ungainly. Fortunately, the language helps us here. If the receiver p is a variable of type Point but the method requires a *Point receiver, we can use this shorthand:

p.ScaleBy(2) 

and the compiler will perform an implicit &p on the variable. This works only for variables, including struct fields like p.X and array or slice elements like perim[0].

A little further down:

But we can call a Point method like Point.Distance with a *Point receiver, because there is a way to obtain the value from the address: just load the value pointed to by the receiver. *The compiler inserts an implicit `` operation for us.** These two function calls are equivalent:

pptr.Distance(q) (*pptr).Distance(q)

and further:

Or the receiver argument has type *T and the receiver parameter has type T . The compiler implicitly dereferences the receiver, in other words, loads the value:

pptr.Distance(q) // implicit (*pptr)

I obviously need to spend more time with this book (very detailed).

atc0005 commented 4 years ago

From Get Programming with Go:

type person struct { 
    name, superpower string
    age int 
} 

timmy := &person{ 
    name: "Timothy", 
    age: 10, 

}

Furthermore, it isn’t necessary to dereference structures to access their fields. The following listing is preferable to writing (*timmy).superpower.

timmy.superpower = "flying" 

fmt.Printf("%+v\n", timmy)

What’s the difference between timmy.superpower and (*timmy).superpower ?

There’s no functional difference because Go automatically dereferences pointers for fields, but timmy.superpower is easier to read and is therefore preferable.