day1-cast 类型转换

引言

还在为golang麻烦的类型转换烦恼吗?

不妨试下 spf13 大神的 cast 吧。

项目地址

项目地址: https://github.com/spf13/cast[star:2.5k]

使用场景

  • 类型转换

安装

go get github.com/spf13/cast

常用方法

  • ToString 转换为字符串
  • ToInt 转换为 int
  • ToBool 转换为布尔值
  • ToTime 转换为 time.Time
  • ToStringE 转换为字符串,返回字符串和error

例子

package day001

import (
	"testing"

	"github.com/spf13/cast"
)

func TestCast(t *testing.T) {
	t.Log("cast.ToString")
	t.Log(cast.ToString("https://oscome.cn"))
	t.Log(cast.ToString(8.88))
	t.Log(cast.ToString([]byte("https://oscome.cn")))
	var abc interface{} = "https://oscome.cn"
	t.Log(cast.ToString(abc))

	t.Log("cast.ToInt")
	t.Log(cast.ToInt("8"))

    // 后面+E 可以多返回一个 error 参数
	t.Log(cast.ToInt64E("8.99"))

	t.Log("cast.ToInt")
	t.Log(cast.ToBool("1"))
	t.Log(cast.ToBool("8.99"))
}

效果如下:

cast

cast 能做的不止如此,除了常见类型,还提供了时间类方法 ToTime、ToDuration,甚至还有切片类转换 ToStringSlice、ToStringMap,强大又好用。自己动手试一试吧!

func TestCastMore(t *testing.T) {
	t.Log("cast.ToTime")
	t.Log(cast.ToTime("2022-01-02 01:01:01 +0000 UTC"))

	t.Log("cast.ToDuration")
	t.Log(cast.ToDuration(1e9))

	t.Log("cast.ToStringSlice")
	t.Log(cast.ToStringSlice([]int{1, 3}))
}

实例代码

https://github.com/oscome/godaily/tree/main/day001

tips

  1. 部分字符转换可能不如意,比如 cast.ToInt64E(“8.99”) 得出的是0,而不是8or9,原因可以尝试读一下源码,也不复杂。
  2. 方法后+E,可以多返回一个参数 error,比如 ToIntE。

源码解读

cast 库的源码比较简单,熟悉语法的应该都能看明白。

我们以 ToFloat64 为 🌰 ,解析我就放注释了

// 直接调用 ToFloat64E
func ToFloat64(i interface{}) float64 {
	v, _ := ToFloat64E(i)
	return v
}

func ToFloat64E(i interface{}) (float64, error) {
	i = indirect(i)

	// toInt 里类型声明,case 判断,没什么好说的
	intv, ok := toInt(i)
	if ok {
		return float64(intv), nil
	}

	// 类型判断
	switch s := i.(type) {
	case float64:
		return s, nil
	case float32:
		return float64(s), nil
	...
	case string:
		// 一般使用的 string 到 float64 的类型转换方法
		v, err := strconv.ParseFloat(s, 64)
		if err == nil {
			return v, nil
		}
		return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
	case json.Number:
		v, err := s.Float64()
		if err == nil {
			return v, nil
		}
		return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
	case bool:
		if s {
			return 1, nil
		}
		return 0, nil
	case nil:
		return 0, nil
	default:
		return 0, fmt.Errorf("unable to cast %#v of type %T to float64", i, i)
	}
}

func indirect(a interface{}) interface{} {
	if a == nil {
		return nil
	}

	// 通过反射获取类型信息,具体类型可见 https://golang.google.cn/pkg/reflect/#Kind
	if t := reflect.TypeOf(a); t.Kind() != reflect.Ptr {
		// 不是指针类型,直接返回
		return a
	}

	// ValueOf 返回一个新值,初始化为存储在接口 a 中的具体值。 ValueOf(nil) 返回零值。
	v := reflect.ValueOf(a)

	// 这里可以简单理解成解一次指针的引用
	// Elem 返回接口 v 包含的值或指针 v 指向的值。如果 v 的 Kind 不是接口或指针,会报 panic 。如果 v 为零,则返回零值。
	for v.Kind() == reflect.Ptr && !v.IsNil() {
		v = v.Elem()
	}

	// 返回 v 当前值作为的接口
	return v.Interface()
}

关注和赞赏都是对小欧莫大的支持! 🤝 🤝 🤝
公众号