Goにおいてポインタは、他の変数のアドレスを保持する変数です。一部の言語とは異なり、Goではポインタ算術ができないため、安全性が高まります。Goではポインタを主に以下の目的で使用します:
GoにはC++のような参照型の明示的なサポートはありませんが、スライスやマップは本質的に参照であり、頻繁に値渡しされます。
package main import "fmt" type User struct { Name string } func changeNameByValue(u User) { u.Name = "ヴァシー" } func changeNameByPointer(u *User) { u.Name = "ペトヤ" } func main() { user := User{Name: "イワン"} changeNameByValue(user) fmt.Println(user.Name) // イワン changeNameByPointer(&user) fmt.Println(user.Name) // ペトヤ }
Goではポインタが定数やリテラルを指すことができますか?
正しい答え: いいえ、Goでは定数やリテラルのアドレスを直接取得することはできません。アドレスを取得できるのは変数だけです:
x := 5 p := &x // OK p2 := &10 // コンパイルエラー
これは、他の言語では許可されているため、多くの人が混同します。
物語
あるプロジェクトで、開発者はスライスの要素への参照を渡そうとし、安全にポインタを通じてスライスを変更できると考えました。しかし、スライスは自身のポインタを含んでいるため、append()メソッドの後に基礎となる配列のアドレスが変更され、変更が元のデータに反映されないことがありました。これにより、微妙なバグが発生しました(データが「失われた」)。
物語
プロジェクトでは、ループ内のローカル変数のポインタリストが作成され、ループを抜けた後、すべてのポインタが同じ変数(ループの反復変数)を指していることがわかりました。エラーは本番環境でのクラッシュ後にしか気づかれませんでした。
物語
開発者は、ポインタの代入が前のオブジェクトのメモリを解放すると思い込み、ポインタに明示的にnilを代入してGCをすぐに期待しました。しかし、実際にはGoのガベージコレクターはオブジェクトを掃除するタイミングを自分で決定し、参照の早期のクリアを無視します。これは、メモリの適時解放を期待した場合のメモリリークにつながりました。