アドベントカレンダー2の7日目の記事です。思い込んでいたことが、実は違うということはよくあります。666不吉な数字が現れてしまいました。お察しの通り、これはdoThing関数が何やら良くないことをしています。iとしてint型の値にアサインされています。このときに起こっていることを整理します。Goにおいてinterface型の値の内部は以下の2つで構成されています。型データポインタ実データポインタinterface型の内部に関してはGoの公式FAQでの以下の内容が理解の助けになります。T.そのため、outはInt値を持っていてもただのInt型値ではなく、上記のような2つのデータを持つ値として保持されます。コード内のefaceはunsafe.Pointerを使うことでinterface型をユーザ側で型定義しています。よってoutの「実データポインタ」が変数pへアサインされています。することで型データがintであるinterface型データoutの実データが666になります。これだけではただoutの値が書き換わっただけです。doThingが呼ぶことになる関数f(out)内のvalまでもが666でPrintf表示されてしまった理由はなぜでしょう。理由として、interface型内のint型の場合、uint8サイズ分つまり0-255までの範囲は共通メモリ領域が存在しており、特定スコープ範囲内では常にその領域が参照されるようになっているためのようです。[1]そしてfmt.Printfの引数としてvalが渡されるとき、Printf内では値はdoThingと同様にinterface型値でのintの形式で渡されます。上述のとおりdoThing内でinterface型内のint型共通メモリ領域の255の中身が666に書き換えられてしまっているため、Printfでは666が表示されてしまいました。逆に、interface型ではないピュアなint型値であるval自体はきちんと255として参照されます。上述の再現条件について、実際に上述のコードにて255から256に置換した場合で実行すると書き換えが行われません。256また、doThingが引数で受け取る関数に渡すinterface型の値をoutではない値にした場合(例えばf(out)からf(i)のように変更する)でも、実行すると書き換えが行われません。outが共通メモリ領域と紐付いており、それを引数として関数に渡すと副作用としてその関数も影響されるようになることがわかります。255まとめinterface型の変数へ値をアサインした場合の特徴を突いたGoのハックでした。参考https://philpearl.github.io/post/anathema/https://syslog.ravelin.com/what-is-an-interface-ee67b3cc9e97https://pkg.go.dev/cmd/compile#hdr-Command_Line脚注この部分に関する証拠を調査したのですが見つけられていません。Go/AWS/K8sバッジを贈って著者を応援しようバッジを受け取った著者にはZennから現金やAmazonギフトカードが還元されます。Proよくある質問LinksX(Twitter)GitHubメディアキットLegal利用規約プライバシーポリシ。