10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Go 2Advent Calendar 2020

Day 5

【Go言語】 unsafe.Pointerでカプセル化を破壊する

Last updated at Posted at 2020-12-04

突然ですが…

外部のパッケージにこんな 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フィールドにアクセスできたら問題解決するんですけど!」っていうことって、皆さんも経験したことあるんじゃないですか?そんな時にはこういうワザもあるということを覚えておくと便利ですよ。

でも、乱用はダメ、絶対!

まとめ

カプセル化の破壊は、要領・用法をまもって正しくお使いください。

  1. conversion。C言語にならってキャスト(cast)と呼ぶ人もいますが、Go言語での正確な呼び方はconversionです。

  2. 自分に限らず他の人のPRもずっと放置されているので、外部からのPRを受け付けてないだけと思われます。

10
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?