https://mattn.kaoriya.net/software/lang/go/20220907112622.htm
Go ではバイト列と文字列は異なる内部データとして扱っています。[]byte
から string
へ変換したり、またその逆を行う際にはキャストが必要になります。ですので string はイミュータブルになります。
しかしイミュータブルなのは理解しつつもバイト列を文字列にする為に無駄なアロケートをしたくない場合もあります。これまで Go ではドキュメントに明文化していなかった為に色々な作法が生まれてしまっていました。その代表的な物が以下です。
s := *(*string)(unsafe.Pointer(&b))
本来は、Go のバイト列の内部は SliceHeader
という構造体により管理されています。
type SliceHeader struct {
また string は以下の StringHeader
で管理されています。
type StringHeader struct {
上記の unsafe.Pointer を使ったコードはこの struct の先頭2フィールドを無理やり参照する事で実現しています。実際に Go のコードでも使われており、これが公式の方法だと思って多くの人が使ってしまっていました。
go/builder.go at 3058d38632aea679c96cd41156b2751c97578a2d · golang/go · GitHub
しかしながらこの実装は、今後 Go の文字列とバイト列に関する最適化の妨げになり得ます。例えば StringHeader と SliceHeader の交換を最適化する事ができなくなります。
そこで今回、unsafe.StringData、unsafe.String、unsafe.SliceData が入りました。これによりバイト列から文字列、またはその逆がミュータブルに変換できる様になりました。当然ですがこれらはバイト列の破壊的な変更により意図しない文字列の変更が行われる為、慎重に取り扱わなければなりません。
func StringToBytes(s string) []byte {
unsafe.StringData は string が持っているバイト列へのポインタが返されます。
fmt.Println(unsafe.StringData("Hello"))
これを実行すると以下の様に表示され、Go のコンパイラが文字列リテラル Hello の2つをまとめて保持しているのが分かります。
0xc67760