Here's something that someone new to Go might run up against. I know I did. The trivial program below, derived from "The Go Programming Language" book, shows two methods that differ only in the way they treat their receiver. In the first, the receiver is a pointer to a slice, and in the second, the receiver is a slice. So far so good. What confused me is that both methods return the same result even when called the same way. I would have expected only one of the methods to work. Here's what I believe is the explanation. The book says (slightly edited) "If the receiver p is a variable of type Path but the method requires a *Path receiver, we can use this shorthand: p.pr1() and the compiler will perform an implicit &p on the variable.". This explains what happens when pr1() is called. I'm surprised that Go would include this implicit behavior because type conversions are generally required to be explicit. Jon Forrest ------- package main import ( "fmt" ) type Path []byte func (p *Path) pr1() { fmt.Printf("%s\n", *p) } func (p Path) pr2() { fmt.Printf("%s\n", p) } func main() { var p Path p = Path("abc") p.pr1() p = Path("abc") p.pr2() }
Here's something that someone new to Go might run up against. I know I did.
The trivial program below, derived from "The Go Programming Language" book, shows two methods that differ only in the way they treat their receiver. In the first, the receiver is a pointer to a slice, and in the second, the receiver is a slice. So far so good. What confused me is that both methods return the same result even when called the same way. I would have expected only one of the methods to work.
Here's what I believe is the explanation. The book says (slightly edited) "If the receiver p is a variable of type Path but the method requires a *Path receiver, we can use this shorthand: p.pr1() and the compiler will perform an implicit &p on the variable.". This explains what happens when pr1() is called.
I'm surprised that Go would include this implicit behavior because type conversions are generally required to be explicit.
I think you are looking for this statement in the Go spec:
A method call x.m() is valid if the method set of (the type of) x
contains m and the argument list can be assigned to the parameter list
of m. If x is addressable and &x's method set contains m, x.m() is
shorthand for (&x).m()
This can be found at https://golang.org/ref/spec#Calls near the bottom
of that section. This was clearly an intentional design decision.
It's true it is an exception, it's one of the few cases where the language adds a pinch of syntactic sugar to make the experience more pleasurable.
I can imagine without this the number one oft repeated feature request would be to _not_ have to write (&t).m() all the time when you just wanted to write t.m().
type Point struct {
*Piece // nil for no piece
AbsPoint
}
// Absolute Point represents a specific point on the board.
type AbsPoint struct {
File uint8
Rank uint8
}
type Piece struct {
Kind
Orientation
Base Kind
Moved bool `json:"-"`
}
type Kind int
const (
King Kind = iota + 1
Queen
Rook
Bishop
...
for _, point := range set {
if point.Piece != nil {
if (point.Kind == King) && (point.Moved == false) && (point.Orientation == mover) {
// do something
break
}
}
}
// instead of
for _, point := range set {
if point.Piece != nil {
if (*(point.Piece).Kind == King) && (*(point.Piece).Moved == false) && (*(point.Piece).Orientation == mover) {
// or if (point.Piece->Kind == King) && (point.Piece->Moved == false) && (point.Piece->Orientation == mover) {
// do something
break
}
}
}