欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang1.21新特性全面講解

 更新時間:2023年08月10日 10:28:46   作者:apocelipes  
經(jīng)過了半年左右的開發(fā),golang?1.21?最近正式發(fā)布了,這個版本中有不少重要的新特性和變更,尤其是在泛型相關(guān)的代碼上,下面小編就來和大家好好嘮嘮吧

經(jīng)過了半年左右的開發(fā),golang 1.21 在今天早上正式發(fā)布了。

這個版本中有不少重要的新特性和變更,尤其是在泛型相關(guān)的代碼上。

因?yàn)橛胁簧俅笞儎?,所以建議等第一個patch版本也就是1.21.1出來之后再進(jìn)行升級,以免遇到一些意外的bug帶來麻煩。

好了,一起來看看1.21帶來的新特性吧。

新的內(nèi)置函數(shù)

1.21添加了三個新的內(nèi)置函數(shù): min 、 max clear 。

min 、 max 如其字面意思,用了選出一組變量里(數(shù)量大于等于1,只有一個變量的時候就返回那個變量的值)最大的或者最小的值。兩個函數(shù)定義是這樣的:

func min[T cmp.Ordered](x T, y ...T) T
func max[T cmp.Ordered](x T, y ...T) T

注意那個類型約束,這是新的標(biāo)準(zhǔn)庫里提供的,原型如下:

type Ordered interface {
    ~int | ~int8 | ~int16 | ~int32 | ~int64 |
    ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr |
    ~float32 | ~float64 |
    ~string
}

也就是說只有基于所有除了map,chan,slice以及復(fù)數(shù)之外的基本類型的變量才能使用這兩個函數(shù)?;蛘邠Q句話說,只有可以使用 < 、 > <= 、 >= 、 == != 進(jìn)行比較的類型才可以使用min和max。

有了min和max,可以把許多自己手寫的代碼替換成新的內(nèi)置函數(shù),可以少寫一些幫助函數(shù)。而且使用新的內(nèi)置函數(shù)還有一個好處,在變量個數(shù)比較少的時候還有編譯器的優(yōu)化可用,比自己寫min函數(shù)性能上要稍好一些。

使用上也很簡單:

numbers := []int{7, 6, 5, 4, 3, 2, 1}
maxIntValue := max(0, numbers...) // 7 type int
minIntValue := min(8, numbers...) // 1 type int

目前max和min都不支持slice的解包操作: max(1, numbers...) ,暫時不知是bug還是規(guī)定,先觀望一陣子。

對于clear來說事情比min和max復(fù)雜。clear只接受slice和map,如果是對泛型的類型參數(shù)使用clear,那么類型參數(shù)的type set必須是map或者slice,否則編譯報錯。

clear的定義如下:

func clear[T ~[]Type | ~map[Type]Type1](t T)

對于參數(shù)是map的情況,clear會刪除所有map里的元素(不過由于golang的map本身的特性,map存儲的數(shù)據(jù)會被正常銷毀,但map已經(jīng)分配的空間不會釋放):

func main() {
        m := map[int]int{1:1, 2:2, 3:3, 4:4, 5:5}
        fmt.Println(len(m))  // 5
        clear(m)
        fmt.Println(len(m))  // 0
}

燃而對于slice,它的行為又不同了:會把slice里所有元素變回零值。看個例子:

func main() {
        s := make([]int, 0, 100) // 故意給個大的cap便于觀察
        s = append(s, []int{1, 2, 3, 4, 5}...)
        fmt.Println(s) // [1 2 3 4 5]
        fmt.Println(len(s), cap(s)) // len: 5; cap: 100
        clear(s)
        fmt.Println(s) // [0 0 0 0 0]
        fmt.Println(len(s), cap(s)) // len: 5; cap: 100
}

這個就比較反直覺了,畢竟clear首先想到的應(yīng)該是把所有元素刪除。那它的意義是什么呢?對于map來說意義是很明確的,但對于slice來說就有點(diǎn)繞彎了:

slice的真實(shí)大小是cap所顯示的那個大小,如果只是用 s := s[:0] 來把所有元素“刪除”,那么這些元素實(shí)際上還是留在內(nèi)存里的,直到s本身被gc回收或者往s里添加新元素把之前的對象覆蓋掉,否則這些對象是不會被回收掉的,這一方面可以提高內(nèi)存的利用率,另一方面也會帶來泄露的問題(比如存儲的是指針類型或者包含指針類型的值的時候,因?yàn)橹羔樳€存在,所以被指向的對象始終有一個有效的引用導(dǎo)致無法被回收),所以golang選擇了折中的辦法:把所有已經(jīng)存在的元素設(shè)置成0值

如果想安全的正常刪除slice的所有元素,有想復(fù)用slice的內(nèi)存,該怎么辦?答案是:

s := make([]T, 0, 100) // 故意給個大的cap便于觀察
s = append(s, []T{*new(T), *new(T)}...)
clear(s)
s = s[:0]

如果沒有內(nèi)置函數(shù)clear,那么我們得自己循環(huán)一個個賦值處理。而有clear的好處是,編譯器會直接用memset把slice的內(nèi)存里的數(shù)據(jù)設(shè)置為0,比循環(huán)會快很多。有興趣的可以看看clear在slice上的實(shí)現(xiàn):代碼在這 。

類型推導(dǎo)更加智能

其實(shí)就是bug修復(fù),以前類似這樣的代碼在某些情況下沒法正常進(jìn)行推導(dǎo):

func F[T ~E[], E any](t T, callable func(E))
func generic[E any](e E) {}
F(t, generic) // before go1.21: error; after go1.21: ok

理論上只要能推導(dǎo)出E的類型,那么 F generic 的所有類型參數(shù)都能推導(dǎo)出來,哪怕 generic 本身是個泛型函數(shù)。以前想正常使用就得這么寫: F(t, generic[Type])

所以與其說是新特性,不如說是對類型推導(dǎo)的bug修復(fù)。

針對類型推導(dǎo)還有其他一些修復(fù)和報錯信息的內(nèi)容優(yōu)化,但這些都沒上面這個變化大,所以恕不贅述。

panic的行為變化

1.21開始如果goroutine里有panic,那么這個goroutine里的defer里調(diào)用的recover必然不會返回nil值。

這導(dǎo)致了一個問題:recover的返回值是傳給panic的參數(shù)的值, panic(nil) 這樣的代碼怎么辦?

先要提醒一下, panic(nil) 本身是無意義的,且會導(dǎo)致recover的調(diào)用方無法判斷究竟發(fā)生了什么,所以一直是被各類linter包括 go vet 命令警告的。然而這么寫語法上完全正確,所以只有警告并不能解決問題。

解決辦法是,如果現(xiàn)在使用 panic(nil) 或者 panic(值為nil的接口) ,recover會收到一個新類型的error: *runtime.PanicNilError 。

總體上算是解決了問題,然而它把有類型的但值是nil的接口也給轉(zhuǎn)換了,雖然從最佳實(shí)踐的角度來講panic一個空值的接口是不應(yīng)該的,但多少還是會給使用上帶來一些麻煩。

所以目前想要禁用這一行為的話,可以設(shè)置環(huán)境變量: export GODEBUG=panicnil=1 。如果go.mod里聲明的go版本小于等于1.20,這個環(huán)境變量的功能自動啟用。

對于modules的變化,會在下一節(jié)講解。

modules的變化

最大的變化是現(xiàn)在mod文件里寫的go版本號的意義改變了。

變成了:mod文件里寫的go的版本意味著這個mod最低支持的golang版本是多少。

比如:

module github.com/apocelipes/flatmap
go 1.21.0

意味著這個modules最低要求go的版本是 go1.21.0 ,而且可以注意到,現(xiàn)在patch版本也算在內(nèi)里,所以一個聲明為 go 1.21.1 的modules沒法被1.21.0版本的go編譯。

這么做的好處是能嚴(yán)格控制自己的程序和庫可以在哪些版本的golang上運(yùn)行,且可以推動庫版本和golang本身版本的升級。

如果嚴(yán)格按照官方要求使用語義版本來控制自己的modules的話,這個改動沒有什么明顯的壞處,唯一的缺點(diǎn)是只有1.21及更高版本的go工具鏈才有這樣的功能。

這個變更對 go.work 文件同樣適用。

包初始化順序的改變

現(xiàn)在按新的順序來初始化包:

  • 把所有的packages按導(dǎo)入路徑進(jìn)行排序(字符串字典順序)存進(jìn)一個列表
  • 按要求和順序找到列表里第一個符合條件的package,要求是這個package所有的import的包都已經(jīng)完成初始化
  • 初始化這個找到的包然后把它移出列表,接著重復(fù)第二步
  • 列表為空的時候初始化流程結(jié)束

這樣做的好處是包的初始化順序終于有明確的標(biāo)準(zhǔn)化的定義了,壞處有兩點(diǎn):

  • 以前的程序如果依賴于特定的初始化順序,那么在新版本會出問題
  • 現(xiàn)在可以通過修改package的導(dǎo)入路徑(主要能改的部分是包的名字)和控制導(dǎo)入的包來做到讓A包始終在B包之前初始化,因此B包的初始化流程里可以對A包公開出來的接口或者數(shù)據(jù)進(jìn)行修改,這樣做耦合性很高也不安全,尤其是B包如果是某個包含惡意代碼的包的話。

我們能做的只有遵守最佳實(shí)踐:不要依賴特定的包直接的初始化順序;以及在使用某個第三方庫前要仔細(xì)考量。

編譯器和runtime的變化

runtime的變化上,gc一如既往地得到了小幅度優(yōu)化,現(xiàn)在對于gc壓力較大的程序來說gc延遲和內(nèi)存占用都會有所減少。

cgo也有優(yōu)化,現(xiàn)在cgo函數(shù)調(diào)用最大可以比原先快一個數(shù)量級。

編譯器的變化上比較顯著的是這個:PGO已經(jīng)可以正式投入生產(chǎn)使用。使用教程

PGO可以帶來6%左右的性能提升,1.21憑借PGO和上個版本的優(yōu)化現(xiàn)在不僅沒有了泛型帶來的編譯速度減低,相比以前還有細(xì)微提升。

還有最后一個變化,這個和編譯器關(guān)系:現(xiàn)在沒有被使用的全局的map類型的變量(需要達(dá)到一定大小,且初始化的語句中沒有任何副作用會產(chǎn)生),現(xiàn)在編譯完成的程序里不會在包含這個變量。因?yàn)閙ap本身占用內(nèi)存且初始化需要花費(fèi)一定時間(map越大花的時間越多)。這個好處是很實(shí)在的,既可以減小產(chǎn)生的二進(jìn)制可執(zhí)行文件的大小,又可以提升運(yùn)行性能。但有個缺點(diǎn),如果有什么程序要依賴編譯好的可執(zhí)行文件內(nèi)部的某些數(shù)據(jù)的話,這個變更可能會帶來麻煩,普通用戶可以忽略這點(diǎn)。

新標(biāo)準(zhǔn)庫

這個版本添加了大把的新標(biāo)準(zhǔn)庫,一起來看看。

log/slog和testing/slogtest

官方提供的結(jié)構(gòu)化日志庫。

可以通過實(shí)現(xiàn) slog.Handler 來定義自己的日志處理器,可以把日志轉(zhuǎn)換成json等格式。標(biāo)準(zhǔn)庫自帶了很多預(yù)定義的處理器,比如json的:

logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))
logger.Info("hello", "count", 3)
// {"time":"2023-08-09T15:28:26.000000000+09:00","level":"INFO","msg":"hello","count":3}

簡單得說,就是個簡化版的zap,如果想使用最基礎(chǔ)的結(jié)構(gòu)化日志的功能,又不想引入zap這樣的庫,那么slog是個很好的選擇。

testing/slogtest里有幫助函數(shù)用來測試自己實(shí)現(xiàn)的日志處理器是否符合標(biāo)準(zhǔn)庫的要求。

slices和maps

golang.org/x/exp/slices golang.org/x/exp/maps 引入了標(biāo)準(zhǔn)庫。

slices庫提供了排序、二分查找、拼接、增刪改查等常用功能,sort這個標(biāo)準(zhǔn)庫目前可以停止使用用slices來替代了。

maps提供了常見的對map的增刪改查拼接合并等功能。

兩個庫使用泛型,且針對golang的slice和map進(jìn)行了細(xì)致入微的優(yōu)化,性能上比自己寫的版本有更多優(yōu)勢,比標(biāo)準(zhǔn)庫sort更是有數(shù)量級的碾壓。

這兩個庫本來1.20就該被接收進(jìn)標(biāo)準(zhǔn)庫了,但因?yàn)樾枰匦略O(shè)計(jì)api和進(jìn)行優(yōu)化,所以拖到1.21了。

cmp

這個也是早該進(jìn)入標(biāo)準(zhǔn)庫的,但拖到了現(xiàn)在。隨著slices、maps和新內(nèi)置函數(shù)都進(jìn)入了新版本,這個庫想不接收也不行了。

這個庫一共有三個東西: Ordered 、 Less 、 Compare

最重要的是 Ordered ,它是所有可以使用內(nèi)置運(yùn)算符進(jìn)行比較的類型的集合。

Less Ordered 顧名思義用來比大小的,且只能比 Ordered 類型的大小。

之所以還有單獨(dú)造出這兩個函數(shù),是因?yàn)樗麄儗an有檢查,比如:

// Less reports whether x is less than y.
// For floating-point types, a NaN is considered less than any non-NaN,
// and -0.0 is not less than (is equal to) 0.0.
func Less[T Ordered](x, y T) bool {
	return (isNaN(x) && !isNaN(y)) || x < y
}

所以在泛型函數(shù)里不知道要比較的數(shù)據(jù)的類型是不是有float的時候,用cmp里提供的函數(shù)是最安全的。這就是他倆存在的意義。

但如果可以100%確定沒有float存在,那么就不應(yīng)該用 Less 等,應(yīng)該直接用運(yùn)算符去比較,因?yàn)榇蠹叶伎吹?,Less和直接比較相比效率是較低的。

已有的標(biāo)準(zhǔn)庫的變化

因?yàn)槭撬儆[,所以我只挑重點(diǎn)說。

bytes

bytes.Buffer 添加了 AvailableBuffer Available 兩個方法,分別返回目前可用的buf切片和可用的長度。主要可以配合 strconv.AppendInt 來使用,直接把數(shù)據(jù)寫入buffer對應(yīng)的內(nèi)存里,可以提升性能。不要對 AvailableBuffer 返回的切片擴(kuò)容,否則必然踩坑。

context

新的 context.WithoutCancel 會把原來的 context.Context 復(fù)制一份,并去除cancel函數(shù),這意味著原先被復(fù)制的上下文取消了這個新的上下文也將繼續(xù)存在。例子:

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    newCtx := context.WithoutCancel(ctx)
    cancel()
    <-ctx.Done() // ok, ctx has cancled.
    <-newCtx.Done() // error: dead lock!
}

之所以會死鎖,是因?yàn)?newCtx 沒有被取消,Done返回的chan會永遠(yuǎn)阻塞住。而且更根本的, newCtx 無法被取消。

新增了 context.WithDeadlineCause context.WithTimeoutCause ,可以增加超時上下文被取消時的信息:

d := time.Now().Add(shortDuration)
ctx, cancel := context.WithDeadline(context.Background(), d, &MyError{"my message"})
cancel()
context.Cause(ctx) // --> &MyError{"my message"}

雖然不如 context.WithCancelCause 靈活,但也很實(shí)用。

crypto/sha256

現(xiàn)在在x86_64平臺上計(jì)算sha256會盡量利用硬件指令(simd和x86_64平臺的SHA256ROUND等指令),這帶來了3-4倍的性能提升。

net

現(xiàn)在golang在Linux上已經(jīng)初步支持Multipath TCP。有關(guān)Multipath TCP的信息可以在這查閱:https://www.multipath-tcp.org/

reflect

ValueOf現(xiàn)在會根據(jù)逃逸分析把值分配在棧上,以前都是直接分配到堆上的。對于比較小的類型來說可以獲得10%以上的性能提升。利好很多使用反射的ORM框架。

新增了 Value.Clear ,對應(yīng)第一節(jié)的clear內(nèi)置函數(shù),如果type不是map或者slice的話這個函數(shù)和其他反射的方法一樣會panic。

runtime

最值得一提的變化是新增了 runtime.Pinner

它的能力是可以讓某個go的對象不會gc回收,一直到 Unpin 方法被調(diào)用。這個是為了方便cgo代碼里讓c使用go的對象而設(shè)計(jì)的。

不要濫用這個接口,如果想告訴gc某個對象暫時不能回收,應(yīng)該正確使用 runtime.KeepAlive

runtime/trace現(xiàn)在有了很大的性能提升,因此觀察程序行為的時候開銷更小,更接近程序真實(shí)的負(fù)載。

sync

添加了 OnceFunc OnceValue 、 OnceValues 這三個幫助函數(shù)。主要是為了簡化代碼。

1.21前:

var initFlag sync.Once
func GetSomeThing() {
    initFlag.Do(func(){
        //真正的初始化邏輯
    })
    // 后續(xù)處理
}

現(xiàn)在變成:

var doInit = sync.OnceFunc(func(){
    //真正的初始化邏輯
})
func GetSomeThing() {
    doInit()
    // 后續(xù)處理
}

新代碼要簡單點(diǎn)。

OnceValue OnceValues 是函數(shù)帶返回值的版本,支持一個和兩個返回值的函數(shù)。

errors

新增了 errors.ErrUnsupported 。這個錯誤表示當(dāng)前操作系統(tǒng)、硬件、協(xié)議、或者文件系統(tǒng)不支持某種操作。

目前os,filepath,syscall,io里的一些函數(shù)已經(jīng)會返回這個錯誤,可以用 errors.Is(err, errors.ErrUnsupported) 來檢查。

unicode

升級到了最新的Unicode 15.0.0。

平臺支持變化

新增了wasip1支持,這是一個對WASI(WebAssembly System Interface)的初步支持。

對于macOS,go1.21需要macOS 10.15 Catalina及以上版本。

龍芯上golang現(xiàn)在支持將代碼編譯為c的動態(tài)和靜態(tài)鏈接庫,基本上在龍芯上已經(jīng)可以嘗試投入生產(chǎn)環(huán)境了。

總結(jié)

上面就是我認(rèn)為需要關(guān)注一下的go1.21的新特性,不是所有的變化都羅列在其中。

比如實(shí)驗(yàn)性支持的新的 for-range 語義我就沒寫,這個還在早期實(shí)驗(yàn)階段,后續(xù)可能會被砍掉也可能行為上有極大變化,所以現(xiàn)在花精力關(guān)注不是很值得。

如果對其他變更有興趣,可以看完整的發(fā)版日志 。

不過還是開頭說的那句話,先別急著在生產(chǎn)環(huán)境上升級,可以等到第一個patch版本出來了再說。

到此這篇關(guān)于golang1.21新特性全面講解的文章就介紹到這了,更多相關(guān)golang1.21新特性內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言設(shè)計(jì)模式之實(shí)現(xiàn)觀察者模式解決代碼臃腫

    Go語言設(shè)計(jì)模式之實(shí)現(xiàn)觀察者模式解決代碼臃腫

    今天學(xué)習(xí)一下用?Go?實(shí)現(xiàn)觀察者模式,觀察者模式主要是用來實(shí)現(xiàn)事件驅(qū)動編程。事件驅(qū)動編程的應(yīng)用還是挺廣的,除了我們都知道的能夠用來解耦:用戶修改密碼后,給用戶發(fā)短信進(jìn)行風(fēng)險提示之類的典型場景,在微服務(wù)架構(gòu)實(shí)現(xiàn)最終一致性、實(shí)現(xiàn)事件源A?+?ES
    2022-08-08
  • golang flag包的使用教程

    golang flag包的使用教程

    golang 的 flag 包是用于處理命令行參數(shù)的工具包,我們可以基于這個包來開發(fā)自定義的命令行工具,下面小編就來為大家介紹一下flag包的具體使用吧
    2023-09-09
  • Golang設(shè)計(jì)模式之工廠方法模式講解和代碼示例

    Golang設(shè)計(jì)模式之工廠方法模式講解和代碼示例

    工廠方法是一種創(chuàng)建型設(shè)計(jì)模式, 解決了在不指定具體類的情況下創(chuàng)建產(chǎn)品對象的問題,本文將通過代碼示例詳細(xì)給大家介紹一下Golang工廠方法模式,感興趣的同學(xué)可以參考一下
    2023-06-06
  • golang下的GOPATH路徑問題及解決

    golang下的GOPATH路徑問題及解決

    為了方便,我一般使用task來管理項(xiàng)目的編譯等事項(xiàng),由于才入門go,所以碰到一個問題,以此篇為記,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • Golang中的sync.WaitGroup用法實(shí)例

    Golang中的sync.WaitGroup用法實(shí)例

    這篇文章主要介紹了Golang中的sync.WaitGroup用法實(shí)例,WaitGroup的用途,它能夠一直等到所有的goroutine執(zhí)行完成,并且阻塞主線程的執(zhí)行,直到所有的goroutine執(zhí)行完成,需要的朋友可以參考下
    2015-07-07
  • 聊聊Go語言編譯github上的項(xiàng)目遇到的坑

    聊聊Go語言編譯github上的項(xiàng)目遇到的坑

    這篇文章主要介紹了解決Go語言編譯github上的項(xiàng)目遇到的坑,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 一篇文章說清楚?go?get?使用私有庫的方法

    一篇文章說清楚?go?get?使用私有庫的方法

    這篇文章主要介紹了go?get?如何使用私有庫,本文會明確指出Git?、golang的配置項(xiàng),附送TortoiseGit?+?Git混合配置,需要的朋友可以參考下
    2022-09-09
  • go責(zé)任鏈行為型設(shè)計(jì)模式Chain?Of?Responsibility

    go責(zé)任鏈行為型設(shè)計(jì)模式Chain?Of?Responsibility

    這篇文章主要為大家介紹了go行為型設(shè)計(jì)模式之責(zé)任鏈Chain?Of?Responsibility使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Goland字符串格式化樣式中“\r“的作用詳解

    Goland字符串格式化樣式中“\r“的作用詳解

    這篇文章主要介紹了Goland字符串格式化樣式中“\r“的作用,"\r"起的作用是回到行首,當(dāng)前控制臺輸出,輸出完以后回到當(dāng)前行的行首,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2023-04-04
  • 詳解Golang中select的使用與源碼分析

    詳解Golang中select的使用與源碼分析

    select?是?Go?提供的?IO?多路復(fù)用機(jī)制,可以用多個?case?同時監(jiān)聽多個?channl?的讀寫狀態(tài)。本文將從源碼角度帶大家了解一下select的使用,需要的可以參考一下
    2022-12-12

最新評論