一文帶你了解Golang中類型轉(zhuǎn)換庫cast的使用
0 前言
你是否在使用 Go 的過程中因?yàn)轭愋娃D(zhuǎn)換的繁瑣而苦惱過?
你是否覺得 Go 語言中的類型斷言可能會 panic 而對自己寫的代碼有那么一點(diǎn)點(diǎn)不放心?
如果你有過如上體驗(yàn),并且想要找到一個合適的解決方案的話,那么本文推薦的一個用于類型轉(zhuǎn)換的第三方庫 cast 絕對是一個值得嘗試的選擇。
1 cast
cast 是一個極為簡潔的第三方庫,github 地址:https://github.com/spf13/cast。
項(xiàng)目主頁里的頭兩句介紹就是:
Easy and safe casting from one type to another in Go
Don’t Panic! ... Cast
可見,cast 的主要功能就是類型轉(zhuǎn)換,且沒有 panic。
多說一句,Don't panic 在英語中本身就是一個常用語,表示不要慌張、不要害怕,所以,在這里其實(shí)是一個有意思的雙關(guān)。
2 上手
2.1 安裝引入
這里只講 go mod 的引入方式。
在 go.mod 文件中 require github.com/spf13/cast v1.5.0(目前最新版為 1.5.0),接著用 mod 進(jìn)行 download、tidy 等操作,再在代碼中 import "github.com/spf13/cast" 即可使用 cast 關(guān)鍵字使用 cast 的功能了。
2.2 使用
2.2.1 常規(guī)用法
我們直接通過幾個簡單的例子來體驗(yàn)一下 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我們創(chuàng)建了一個 interface{} 類型的變量 target,傳統(tǒng)方式下如果要將一個 interface{} 轉(zhuǎn)化為 string,需要使用類型斷言:
var target interface{} = "123"
str := target.(string)
// or
str, ok := target.(string)類型斷言的缺點(diǎn)很明顯,如果不接收第二個返回值,會有 panic 風(fēng)險;如果接收第二個參數(shù),則略顯繁瑣。
到了第二個 ToInt,cast 的優(yōu)勢就更明顯了,傳統(tǒng)方式下,一個 interface{} 類型的 "123" 如果要轉(zhuǎn)換成 int,必須先類型斷言為 string,再使用 strconv 轉(zhuǎn)換成 int,代碼就不寫了,想象一下就知道有多麻煩,而 cast 可以將這個過程一步到位。
接著是第三個輸出 cast.ToInt(str),這里的 str 是一個 string 類型的 "hello, world!",它顯然不能被轉(zhuǎn)換成 int,于是 cast 將其設(shè)置為 int 的零值 0。
其實(shí) cast 的所有類型轉(zhuǎn)換都會將無法轉(zhuǎn)換的結(jié)果轉(zhuǎn)為零值,而不是 panic,這也就是 cast 官方承諾的 Don't panic。
2.2.2 帶 error 的用法
看到這里,有朋友可能要問了:如果我的邏輯必須判斷目標(biāo)是否轉(zhuǎn)換成功了呢?如果我的轉(zhuǎn)換結(jié)果就有可能是 0 呢?我怎么知道這個 0 是轉(zhuǎn)換失敗的零值,還是目標(biāo)原始的真實(shí)值?
cast 的作者自然也想到了這一點(diǎn),于是,cast 的所有類型轉(zhuǎn)換函數(shù)都有一個對應(yīng)的 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 的版本其實(shí)就是在非 error 版的函數(shù)名結(jié)尾添加了一個 E,其結(jié)果也很好理解,這里不再展開細(xì)講了。
2.2.3 很酷的東西
最后再來看一個我覺得很酷的東西:
var js interface{} = `{"name": "Jack", "gender": "male"}`
fmt.Println(cast.ToStringMap(js))
// 輸出
map[gender:male name:Jack]cast 能直接將一個 JSON 字符串轉(zhuǎn)換成 map!當(dāng)然這一步其實(shí)用類型斷言也可以做到,但 cast 的方式會更加優(yōu)雅。
3 性能及原理
如果你只是想使用 cast,那么接下來的內(nèi)容就可以忽略了;如果你還想深入了解一些 cast,可以看看這一節(jié)。
很多做后端開發(fā)的朋友會習(xí)慣性關(guān)心性能和原理,我也一樣,所以早在我第一次接觸使用 cast 時,我就去看了它的源碼,然后……這樣,我直接把上面我們用過的 ToInt 的相關(guān)源碼列出來,大家自己看看就明白了:
// 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 函數(shù)里使用反射來獲取 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)
}
// ...省略
}明白了吧,沒有什么技巧,依然是常規(guī)手段進(jìn)行轉(zhuǎn)換,只是它把各種情況都囊括了進(jìn)來,做到了足夠全面。
而且我們發(fā)現(xiàn),帶 error 的函數(shù)才是原始函數(shù),不帶 error 的只是一個封裝后的便捷方式。
cast 的源碼很短,只有兩個文件,加起來不到 2000 行。
所以看到這里,cast 的性能問題就沒什么值得討論的了,一定高不到哪兒去。尤其在泛型已經(jīng)實(shí)裝了之后,泛型的性能要遠(yuǎn)超類型斷言、反射之類的技術(shù),因此大家在使用 cast 的時候也請視情況而定。
4 總結(jié)
cast 是我用了很多年的一個庫了,早在泛型還八字沒一撇的時候我就發(fā)現(xiàn)了這個庫,那時我們的項(xiàng)目代碼里充斥著許多 interface{} 和反射,cast 的確幫了我們很大的忙。盡管現(xiàn)在已經(jīng)是泛型時代,go 語言可以用性能更佳的泛型替代許多以前只能用 interface{} 甚至反射實(shí)現(xiàn)的場景,但依然存在不少我們無法避免要用 interface{} 或類型轉(zhuǎn)換的地方,這種時候,尤其是這段程序?qū)π阅懿幻舾袝r,cast 依然是一把萬金油式的利器。
總結(jié)一下,cast 是一個用于類型轉(zhuǎn)換的 golang 第三方庫,它最大的特點(diǎn)是在類型轉(zhuǎn)換時可以不 panic,而是將出現(xiàn)問題的地方轉(zhuǎn)換成零值。當(dāng)然,cast 也提供了帶 error 的函數(shù),以供開發(fā)者在適當(dāng)情況下使用。cast 的性能可能會是一個問題,因此我們在使用時一定要選擇合適的場景,避免由于濫用 cast 造成的性能瓶頸。
到此這篇關(guān)于一文帶你了解Golang中類型轉(zhuǎn)換庫cast的使用的文章就介紹到這了,更多相關(guān)Golang類型轉(zhuǎn)換庫cast內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Go 1.21新增的slices包中切片函數(shù)用法詳解
Go 1.21新增的 slices 包提供了很多和切片相關(guān)的函數(shù),可以用于任何類型的切片,本文通過代碼示例為大家介紹了部分切片函數(shù)的具體用法,感興趣的小伙伴可以了解一下2023-08-08
實(shí)現(xiàn)像php一樣方便的go ORM數(shù)據(jù)庫操作示例詳解
這篇文章主要為大家介紹了實(shí)現(xiàn)像php一樣方便的go ORM數(shù)據(jù)庫操作示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12
Go中函數(shù)的使用細(xì)節(jié)與注意事項(xiàng)詳解
在Go語言中函數(shù)可是一等的(first-class)公民,函數(shù)類型也是一等的數(shù)據(jù)類型,下面這篇文章主要給大家介紹了關(guān)于Go中函數(shù)的使用細(xì)節(jié)與注意事項(xiàng)的相關(guān)資料,文中通過實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-11-11
gorm整合進(jìn)go-zero的實(shí)現(xiàn)方法
go-zero提供的代碼生成器里面,沒有提供orm框架操作,但是提供了遍歷的緩存操作,所以可以利用gorm當(dāng)作一個sql語句的生成器,把生成后的sql語句放到go-zero生成的模板中去執(zhí)行,對gorm整合進(jìn)go-zero的實(shí)現(xiàn)方法感興趣的朋友一起看看吧2022-03-03
Golang算法問題之?dāng)?shù)組按指定規(guī)則排序的方法分析
這篇文章主要介紹了Golang算法問題之?dāng)?shù)組按指定規(guī)則排序的方法,結(jié)合實(shí)例形式分析了Go語言數(shù)組排序相關(guān)算法原理與操作技巧,需要的朋友可以參考下2017-02-02
在ubuntu下構(gòu)建go語言開發(fā)環(huán)境的方法
這篇文章主要介紹了在ubuntu下構(gòu)建go語言開發(fā)環(huán)境的方法,需要的朋友可以參考下2014-10-10

