介绍
今天我们再来介绍Steve Francia(spf13)大神的另一个库cast。cast
是一个小巧、实用的类型转换库,用于将一个类型转为另一个类型。它提供了一套高效且安全的类型转换功能。 最初开发cast
是用在hugo中的。
快速使用
先安装:
|
|
后使用:
|
|
实际上,cast
实现了多种常见类型之间的相互转换,返回最符合直觉的结果。例如:
nil
转为string
的结果为""
,而不是"nil"
;true
转为string
的结果为"true"
,而true
转为int
的结果为1
;interface{}
转为其他类型,要看它里面存储的值类型。
这些类型包括所有的基本类型(整形、浮点型、布尔值和字符串)、空接口、nil
,时间(time.Time
)、时长(time.Duration
)以及它们的切片类型, 还有map[string]Type
(其中Type
为前面提到的类型):
|
|
基本使用
cast
提供了两组函数:
ToType
(其中Type
可以为任何支持的类型),将参数转换为Type
类型。如果无法转换,返回Type
类型的零值或nil
;ToTypeE
以 E 结尾,返回转换后的值和一个error
。这组函数可以区分参数中实际存储了零值,还是转换失败了。
源码分析:
实现上大部分代码都类似,ToType
在内部调用ToTypeE
函数,返回结果并忽略错误。ToType
函数的实现在文件cast.go
中, 而ToTypeE
函数的实现在文件caste.go
中。
|
|
ToTypeE
函数都接受任意类型的参数(interface{}
),然后使用类型断言根据具体的类型来执行不同的转换。如果无法转换,返回错误。
|
|
首先调用indirect
函数将参数中可能的指针去掉(返回原始类型)。如果类型本身不是指针,那么直接返回。否则返回指针指向的值。 循环直到返回一个非指针的值:
|
|
所以,下面代码输出都是 8:
|
|
时间和时长转换
时间类型的转换代码如下:
|
|
根据传入的类型执行不同的处理:
- 如果是
time.Time
,直接返回; - 如果是整型,将参数作为时间戳(自 UTC 时间
1970.01.01 00:00:00
到现在的秒数)调用time.Unix
生成时间。Unix
接受两个参数,第一个参数指定秒,第二个参数指定纳秒; - 如果是字符串,调用
StringToDateInDefaultLocation
函数依次尝试以下面这些时间格式调用time.Parse
解析该字符串。如果某个格式解析成功,则返回获得的time.Time
。否则解析失败,返回错误; - 其他任何类型都无法转换为
time.Time
。
字符串转换为时间:
|
|
时长类型的转换代码如下:
|
|
根据传入的类型进行不同的处理:
- 如果是
time.Duration
类型,直接返回; - 如果是整型或浮点型,将其数值强制转换为
time.Duration
类型,单位默认为ns
; - 如果是字符串,分为两种情况:如果字符串中有时间单位符号
nsuµmh
,直接调用time.ParseDuration
解析;否则在字符串后拼接ns
再调用time.ParseDuration
解析; - 其他类型解析失败。
时间、时长示例:
|
|
转换为切片
实际上,这些函数的实现基本类似。使用类型断言判断类型。如果就是要返回的类型,直接返回。否则根据类型进行相应的转换。
我们主要分析两个实现:ToIntSliceE
和ToStringSliceE
。ToBoolSliceE/ToDurationSliceE
与ToIntSliceE
基本相同。
首先是ToIntSliceE
:
|
|
根据传入参数的类型:
- 如果是
nil
,直接返回错误; - 如果是
[]int
,不用转换,直接返回; - 如果传入类型为切片或数组,新建一个
[]int
,将切片或数组中的每个元素转为int
放到该[]int
中。最后返回这个[]int
; - 其他情况,不能转换。
ToStringSliceE
:
|
|
根据传入的参数类型:
- 如果是
[]interface{}
,将该参数中每个元素转为string
,返回结果切片; - 如果是
[]string
,不需要转换,直接返回; - 如果是
interface{}
,将参数转为string
,返回只包含这个值的切片; - 如果是
string
,调用strings.Fields
函数按空白符将参数拆分,返回拆分后的字符串切片; - 其他情况,不能转换。
转换为切片示例:
|
|
转为map[string]Type
类型
cast
库能将传入的参数转为map[string]Type
类型,Type
为上面支持的类型。
其实只需要分析一个ToStringMapStringE
函数就可以了,其他的实现基本一样。ToStringMapStringE
返回map[string]string
类型的值。
|
|
根据传入的参数类型:
- 如果是
map[string]string
,不用转换,直接返回; - 如果是
map[string]interface{}
,将每个值转为string
存入新的 map,最后返回新的 map; - 如果是
map[interface{}]string
,将每个键转为string
存入新的 map,最后返回新的 map; - 如果是
map[interface{}]interface{}
,将每个键和值都转为string
存入新的 map,最后返回新的 map; - 如果是
string
类型,cast
将它看成一个 JSON 串,解析这个 JSON 到map[string]string
,然后返回结果; - 其他情况,返回错误。
转换为映射示例:
|
|
总结
cast
库能在几乎所有常见类型之间转换,使用非常方便。代码量也很小,有时间建议读读源码。常用于解析配置数据。
参考
- cast GitHub 仓库
- 原作者:Go 每日一库之 cast