乐趣区

关于go:Golang-类型转换库-cast

0 前言

你是否在应用 Go 的过程中因为类型转换的繁琐而苦恼过?

你是否感觉 Go 语言中的类型断言可能会 panic 而对本人写的代码有那么一点点不释怀?

如果你有过如上体验,并且想要找到一个适合的解决方案的话,那么本文举荐的一个用于类型转换的第三方库 cast 相对是一个值得尝试的抉择。

1 cast

cast 是一个极为简洁的第三方库,github 地址:https://github.com/spf13/cast。

我的项目主页里的头两句介绍就是:

Easy and safe casting from one type to another in Go

Don’t Panic! ... Cast

可见,cast 的次要性能就是 类型转换 ,且 没有 panic

多说一句,Don't panic 在英语中自身就是一个常用语,示意不要慌乱、不要胆怯,所以,在这里其实是一个有意思的双关。

2 上手

2.1 装置引入

这里只讲 go mod 的引入形式。

go.mod 文件中 require github.com/spf13/cast v1.5.0(目前最新版为 1.5.0),接着用 mod 进行 downloadtidy 等操作,再在代码中 import "github.com/spf13/cast" 即可应用 cast 关键字应用 cast 的性能了。

2.2 应用

2.2.1 惯例用法

咱们间接通过几个简略的例子来体验一下 cast:

var target interface{} = "123"
str := "hello, world!"

fmt.Println(cast.ToString(target))
fmt.Println(cast.ToInt(target))
fmt.Println(cast.ToInt(str))

// 输入:123
123
0

咱们创立了一个 interface{} 类型的变量 target,传统形式下如果要将一个 interface{} 转化为 string,须要应用类型断言:

var target interface{} = "123"

str := target.(string)
// or
str, ok := target.(string)

类型断言的毛病很显著,如果不接管第二个返回值,会有 panic 危险;如果接管第二个参数,则略显繁琐。

到了第二个 ToInt,cast 的劣势就更显著了,传统形式下,一个 interface{} 类型的 "123" 如果要转换成 int,必须先类型断言为 string,再应用 strconv 转换成 int,代码就不写了,设想一下就晓得有多麻烦,而 cast 能够将这个过程一步到位。

接着是第三个输入 cast.ToInt(str),这里的 str 是一个 string 类型的 "hello, world!",它显然不能被转换成 int,于是 cast 将其设置为 int 的零值 0

其实 cast 的所有类型转换都会将无奈转换的后果转为零值,而不是 panic,这也就是 cast 官网承诺的 Don't panic

2.2.2 带 error 的用法

看到这里,有敌人可能要问了:如果我的逻辑必须判断指标是否转换胜利了呢?如果我的转换后果就有可能是 0 呢?我怎么晓得这个 0 是转换失败的零值,还是指标原始的实在值?

cast 的作者天然也想到了这一点,于是,cast 的所有类型转换函数都有一个对应的 with error 版:

str := "hello"
strNum := "123"

num, e := cast.ToIntE(str)
fmt.Println(num)
fmt.Println(e)

num, e = cast.ToIntE(strNum)
fmt.Println(num)
fmt.Println(e)

// 输入
0
unable to cast "hello" of type string to int64
123
nil

error 的版本其实就是在非 error 版的函数名结尾增加了一个 E,其后果也很好了解,这里不再开展细讲了。

2.2.3 很酷的货色

最初再来看一个我感觉很酷的货色:

var js interface{} = `{"name": "Jack", "gender": "male"}`

fmt.Println(cast.ToStringMap(js))

// 输入
map[gender:male name:Jack]

cast 能间接将一个 JSON 字符串转换成 map!当然这一步其实用类型断言也能够做到,但 cast 的形式会更加优雅。

3 性能及原理

如果你只是想应用 cast,那么接下来的内容就能够疏忽了;如果你还想深刻理解一些 cast,能够看看这一节。

很多做后端开发的敌人会习惯性关怀性能和原理,我也一样,所以早在我第一次接触应用 cast 时,我就去看了它的源码,而后……这样,我间接把下面咱们用过的 ToInt 的相干源码列出来,大家本人看看就明确了:

// cast.go

// ToInt casts an interface to an int type.
func ToInt(i interface{}) int {v, _ := ToIntE(i)
    return v
}

// caste.go

// ToIntE casts an interface to an int type.
func ToIntE(i interface{}) (int, error) {i = indirect(i)    // 这个 indirect 函数里应用反射来获取 i 的 interface{} 值,代码不列了
    // ... 省略
    switch s := i.(type) {
    case int64:
        return int(s), nil
    case int32:
        return int(s), nil
    // ... 省略
    case string:
        v, err := strconv.ParseInt(trimZeroDecimal(s), 0, 0)
        if err == nil {return int(v), nil
        }
        return 0, fmt.Errorf("unable to cast %#v of type %T to int64", i, i)
    }
    // ... 省略
}

明确了吧,没有什么奇技淫巧,仍然是惯例伎俩进行转换,只是它把各种状况都囊括了进来,做到了足够全面。

而且咱们发现,带 error 的函数才是原始函数,不带 error 的只是一个封装后的便捷形式。

cast 的源码很短,只有两个文件,加起来不到 2000 行。

所以看到这里,cast 的性能问题就没什么值得探讨的了,肯定高不到哪儿去。尤其在泛型曾经实装了之后,泛型的性能要远超类型断言、反射之类的技术,因而大家在应用 cast 的时候也请视状况而定。

4 总结

cast 是我用了很多年的一个库了,早在泛型还八字没一撇的时候我就发现了这个库,那时咱们的我的项目代码里充斥着许多 interface{} 和反射,cast 确实帮了咱们很大的忙。只管当初曾经是泛型时代,go 语言能够用性能更佳的泛型代替许多以前只能用 interface{} 甚至反射实现的场景,但仍然存在不少咱们无奈防止要用 interface{} 类型转换 的中央,这种时候,尤其是这段程序对性能不敏感时,cast 仍然是一把万金油式的利器。

总结一下,cast 是一个用于类型转换的 golang 第三方库,它最大的特点是在类型转换时能够不 panic,而是将呈现问题的中央转换成零值。当然,cast 也提供了带 error 的函数,以供开发者在适当状况下应用。cast 的性能可能会是一个问题,因而咱们在应用时肯定要抉择适合的场景,防止因为滥用 cast 造成的性能瓶颈。

退出移动版