まえがき
Pythonのデータサイエンス寄りのところを再復習してて、飽きてきたので他の言語やりたくなった。
F#, Rust, Nim.......。あげく一周回ってGOをやろう。からの疑問点。
本題
まずは引用を。
golang.jp
レシーバとしてポインタまたは値のどちらを受け取るかによって、次の違いがあります。レシーバとして値を受け取るメソッドでは、ポインタまたは値で呼び出すことができますが、ポインタを受け取るメソッドでは、ポインタでしか呼び出すことができません。これは後者ではメソッド内でレシーバを変更可能であるためです。(複製された値に加えた変更は破棄されてしまうため。)
とあるんですが、go 1.10.3 現在だとそうじゃないっぽい・・・?
package main
import (
"fmt"
)
func main() {
p := &Grid{5, 2}
q := Grid{1, 0}
fmt.Println(p, q)
p.move(-2, 2)
q.move(3, 3)
fmt.Println(p, q)
}
type Grid struct {x, y int}
func (p *Grid) move(x, y int) *Grid{
p.x += x
p.y += y
return p
}
結果がこうなる:
&{5 2} {1 0}
&{3 4} {4 3}
予想:
q
はGrid
構造体のポインタではなく値を束縛してあるので、レシーバにGrid
のポインタ型(*Grid
)を指定しているmove
メソッドが弾かれると予想していた。
実際:
通ったし、ちゃんと破壊的変更されちゃってる。
ええ~~~~~~~~???
もしかして、値で渡しても関数定義の型に従ってポインタが渡されるようになってるのか…?余計なお世話では…?
と思いきや、こちら(関数形式)で渡すとちゃんとエラー吐くことが分かりました。
func main() {
p := &Grid{5, 2}
q := Grid{1, 0}
fmt.Println(p, q)
p.move(-2, 2)
(*Grid).move(q, 3, 3)
fmt.Println(p, q)
}
# command-line-arguments
.\main.go:13:14: cannot use q (type Grid) as type *Grid in argument
to (*Grid).move
「Grid
は*Grid
じゃないからダメ!!!!!!」ってキレてくる。さっきは優しかったのに!
というわけで、値で渡してもそのポインタを自動的に渡してくれる…という挙動はレシーバ限定っぽい。
考察:なんでそんな仕様なんや
思うに、メソッドチェーンを考えてのことかなと(こなみかん)