突然ですが…
外部のパッケージにこんな structがあったと思ってください:
package dokoka
type Nanka struct {
data string
}
func New(d string) *Nanka {
return &Nanka{data:d}
}
func (n Nanka) String() string {
return "(´-`).。oO(" + n.data + ")"
}
そいでもって、別のパッケージからこの data
にアクセスすると、
package main
import (
"fmt"
"dokoka"
)
func main(){
n := dokoka.New("あれ")
n.data = "これ"
fmt.Println(n)
}
怒られます。こんなふうに↓
n.data undefined (cannot refer to unexported field or method data)
そりゃそうですよね。Go言語では小文字ではじまるフィールドは外部からはアクセスできないんですから。(いわゆるprivateフィールドというやつです)
それでも…
どうしてもprivateフィールドにアクセスする必要がある!そんなこともありますよね。
そんな時には unsafe.Pointer
という黒魔術を使うことで privateフィールドというカプセルを破壊することができちゃいます:
n := dokoka.New("あれ")
x := (*struct{ data string })(unsafe.Pointer(n))
x.data = "これ"
fmt.Println(n) // (´-`).。oO(これ)
Go言語では普通、互換性のない型どうしの変換1はできませんが、unsafe.Pointer
は任意の参照型との変換ができるという特殊な特徴を持っています。
そのため、unsafe.Pointer
を経由して同じ構造を持った構造体への参照に変換してやれば、あら不思議 privateだったフィールドにもアクセスできてしまいます。自パッケージ内で定義された構造体なら、privateフィールドでももちろんアクセスできますからね。(※ここでは匿名の構造体に変換しています)
なお前提として、アクセスする対象の 正しいstructの構造を知っている必要があります。structの構造が違っていたり変わったりすると、破滅的な結果をもたらすので注意しましょう。
なんでこんなことする必要が…?
ここからはこんなことをする事になった経緯なんですけどね、go-simplejsonっていう便利なパッケージがありましてね。とっても便利に使わせてもらっていたんです。
で、ある時、encoding/json
にある Decoder
の simplejson版がほしくなったわけです。
サクッと作ろうとしたら、simplejson の構造体は privateフィールドになってて、サクッと作れなかったんですよ↓
type Json struct {
data interface{}
}
privateフィールドにアクセスする必要があったので、simplejson自体に Decoder を追加して pull requestも送った んですけど受理されず2……とはいえ、forkしたバージョンを保守するのもめんどくさい……
だったら、カプセルの中にお邪魔して書き換えちゃえばいいか、と思って別パッケージとして作り直した次第です。
こういう、「ちょっと今だけこのprivateフィールドにアクセスできたら問題解決するんですけど!」っていうことって、皆さんも経験したことあるんじゃないですか?そんな時にはこういうワザもあるということを覚えておくと便利ですよ。
でも、乱用はダメ、絶対!
まとめ
カプセル化の破壊は、要領・用法をまもって正しくお使いください。