🙄

Goでinterface{}からstringに変換するのがこんなに泥臭いはずがない

2023/02/23に公開
1

interface{}からstringに変換

下記のように、interface{}からstringに変換するのは多々見る記述ですよね。

value := interface{}("test")
value.(string)

こういったときどうします?

では、下記のようなmapを同様に要素をすべてstringにしたいときはどうしますか?

values := map[string]interface{}{
		"int":    int(10),
		"float":  float64(10),
		"string": string("hello"),
		"byte":   []byte("hello"),
	}

もちろん下記のような書き方だと、型アサーションに失敗して正しく変換できません。

func main() {
	values := map[string]interface{}{
		"int":    int(10),
		"float":  float64(10),
		"string": string("hello"),
		"byte":   []byte("hello"),
	}

	for i, v := range values {
		fmt.Printf("%s:%s\n", i, v.(string)) // 型アサーションに失敗してpanicを引き起こす
	}
}

たぶん下記のような関数を作成して処理するのではないでしょうか?

func toString(v any) string {
	switch v.(type) {
	case string:
		return v.(string)
	case int:
		return fmt.Sprintf("%d", v)
	case float64:
		return fmt.Sprintf("%g", v)
	case []byte:
		return fmt.Sprintf("%s", v)
	default:
		return ""
	}
}

func main() {
	values := map[string]interface{}{
		"int":    int(10),
		"float":  float64(10),
		"string": string("hello"),
		"byte":   []byte("hello"),
	}

	for i, v := range values {
		fmt.Printf("%s:%s\n", i, toString(v))
	}
}

switchで型ごとに分岐させて、それぞれに合った方法でstringを返すように関数を作って、
また受け入れる型が増えたら分岐を増やして・・・・

必要となるたびに同じような関数作って何度も泥臭い作業して・・・・
「もう嫌になっちゃうね」と思ったそこのあなた!!!

そんなあなたに届けたい

そんなあなたに本日ご紹介したい商品はこちら!!
https://github.com/harakeishi/gats

gats(ガッツ)です。
gatsは、go any to stringの略でGoの基本型で宣言されているinterfaceをstringに変換する関数を提供します。
気合で変換するという意味も込められています。

使い方

さっきの例をgatsを使って書くと下記のようになります。

import (
	"fmt"
	"github.com/harakeishi/gats"
)
func main() {
	values := map[string]interface{}{
		"int":    int(10),
		"float":  float64(10),
		"string": string("hello"),
		"byte":   []byte("hello"),
	}

	for i, v := range values {
		result, _ := gats.ToString(v)
		fmt.Printf("%s:%s\n", i, result)
	}
}

泥臭い作業をgatsに任せたのですっきりしましたね。

終わりに

interface{}からstringに変換する方法を調べた際、結構型アサーションして変換するとかswitch文で分岐してそれぞれに合った方法で変換するとかは見つかったのですが、gatsのようにパッケージ化したというものは見つけられませんでした。
調べた方法で解決できるのはいいのですが、シンプルにより記述量少なく解決できる(なんなら変換の責務を外部に任せられる)方法があればいいなと思ったので今回パッケージ化しました。
このパッケージが少しでも世の人のためになればうれしいです。

Discussion

harachanharachan

[]byte以外下記の内容で対応出来るやんということに気づかせてもらいました
アウトプットって色々フィードバックがあるから大事だな〜
https://go.dev/play/p/D_W5eJkpFKD

values := map[string]interface{}{
	"int":     int(10),
	"float":   float64(10),
	"string":  string("hello"),
	"byte":    []byte("hello"),
	"bool":    true,
	"float64": float64(10),
}

for _, v := range values {
	tmp := fmt.Sprintf("%v", v)
	fmt.Printf("%t:%s\n", tmp, tmp)
}