自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

Go for 循環(huán)有時候真的很坑。。。

開發(fā) 前端
如果實施,本次變更會導(dǎo)致 Go 的前后版本語義有所不同。還不如變成一個 go.mod 文件的一個語義開關(guān),一變?nèi)?,否則這種變一些不變一些的,會給問題排查和理解上帶來不少的成本。

大家好,我是煎魚。

不知道有多少 Go 的面試題和泄露,都和 for 循環(huán)有關(guān)。今天我在周末認(rèn)真一看,發(fā)現(xiàn)了 redefining for loop variable semantics[1] ,看來大家踩到的坑都是一樣的。

著名的硬核大佬 Russ Cox 表示他一直在研究這個問題,表示十年的經(jīng)驗表明了當(dāng)前語義的代價是很大的,得動一動,看看能不能打破兼容性原則。

想了下之前 Go modules 的事情,我真怕他一口氣就把這塔給推了...

問題

案例一

在 Go 語言中,我們寫 for 語句時有時會出現(xiàn)運行和猜想的結(jié)果不一致。例如以下第一個案例的代碼:

var all []*Item
for _, item := range items {
all = append(all, &item)
}

這段代碼有問題嗎?變量 all 內(nèi)的 item 變量,存儲進去的是什么?是每次循環(huán)的 item 值,每次都不一樣,對嗎?

實際上在 for 循環(huán)時,每次存入變量 all 的都是相同的 item,也就是最后一個循環(huán)的 item 值。這是 Go 面試?yán)锝?jīng)常出現(xiàn)的題目,結(jié)合 goroutine 更風(fēng)騷,畢竟還會存在亂序執(zhí)行等問題。

如果你想解決這個問題,就需要把程序改寫成如下:

var all []*Item
for _, item := range items {
item := item
all = append(all, &item)
}

要重新聲明一個局部變量 item 變量,把 for 循環(huán)的 item 變量給存儲下來,再追加進去。

案例二

接下來是第二個案例的代碼:

var prints []func()
for _, v := range []int{1, 2, 3} {
prints = append(prints, func() { fmt.Println(v) })
}
for _, print := range prints {
print()
}

這段程序的輸出結(jié)果是什么?沒有 & 取地址符,是輸出 1,2,3 嗎?

結(jié)果程序一運行,輸出結(jié)果是 3,3,3。這又是為什么?

問題的重點之一:關(guān)注到閉包函數(shù),實際上所有閉包都打印的是相同的 v,也就是輸出 3,原因是在 for 循環(huán)結(jié)束后,最后 v 的值被設(shè)置為了 3,僅此而已。

如果想要達到預(yù)期的效果,依然是使用萬能的再賦值。改寫后的代碼如下:

for _, v := range []int{1, 2, 3} {
v := v
prints = append(prints, func() { fmt.Println(v) })
}

增加 v := v 語句,程序輸出結(jié)果為 1,2,3。仔細(xì)翻翻你寫過的 Go 工程,是不是都很熟悉?就這改造方法,贏了。

尤其是配合上 Goroutine 的寫法,很多同學(xué)會更容易在此翻車。

解決方案

修復(fù)思路

實際上 Go 核心團隊在內(nèi)部和社區(qū)已經(jīng)討論過許久,希望重新定義 for 循環(huán)的語義。要達到的目的是:使循環(huán)變量每次迭代而不是每次循環(huán)。

解決的辦法是:在每個迭代變量 x 的每個循環(huán)體開頭,加一個隱式的再賦值,也就是 x := x,就能夠解決上述程序中所隱含的坑。

和我們現(xiàn)在做的一樣,只不過我們是自己手動加的,Go 團隊做的是希望在編譯器內(nèi)隱式處理。

讓用戶自己決定

比較尷尬的是 Go 團隊在 Proposal: Go 2 transition[2] 中明確禁止重新定義語言的語義,所以 rsc 不能直接這么干。

因此 rsc 打算開個新坑,希望將會由用戶自己決定控制這個 “破壞”,方式將會是根據(jù)每個 modules 的 go.mod 文件中的 go 行(版本聲明)來決定語義。

例如,如果是在 Go1.30 對本文討論的 for 循環(huán)將循環(huán)變量改為迭代,那么在 go.mod 文件中的 go 版本聲明是將是一個關(guān)鍵的開關(guān)。

如下圖示:

圖片

像上圖的配置,Go 1.30 或更高版本將會每次迭代變量,而早期 Go 版本的將每次循環(huán)變量,也就是 go.mod 的 Go 版本控制了新特性的語義,不同 modules 都可能會因此不一樣。

如此一來上述提到的 for 循環(huán)問題都會在一定范圍內(nèi)被解決。

總結(jié)

for 循環(huán)時的變量問題,一直是各大 Go 考官愛考的題目,也確實在實際編程 Go 代碼時會遇到這類坑。

雖然 rsc 希望在 go.mod 文件上開創(chuàng)先河,利用 go 版本的聲明,去修改語義(不允許添加和刪除)。這無疑是給 Go1 兼容性保障開了一個后門。

如果實施,本次變更會導(dǎo)致 Go 的前后版本語義有所不同。還不如變成一個 go.mod 文件的一個語義開關(guān),一變?nèi)儯駝t這種變一些不變一些的,會給問題排查和理解上帶來不少的成本。

這顯然是一個很折騰人的思考題。

責(zé)任編輯:武曉燕 來源: 腦子進煎魚了
相關(guān)推薦

2019-11-05 09:20:06

SQLiteLinux

2013-04-08 15:39:15

程序員

2010-02-23 16:21:24

Python Win

2018-10-30 12:44:04

Linux系統(tǒng)內(nèi)存

2019-12-06 17:31:30

程序員人生第一份工作設(shè)計

2010-01-20 10:14:53

C++程序

2013-12-11 09:29:02

2014-10-24 10:10:33

UbuntuUbuntu 14.1

2010-03-10 11:14:56

智能交換機

2010-03-17 14:50:06

智能交換機

2009-09-28 11:20:30

面試

2010-08-30 10:37:04

云計算

2023-05-22 07:10:38

GPTpromptPerplexity

2010-03-10 16:51:21

以太網(wǎng)交換機

2021-08-14 08:10:58

物聯(lián)網(wǎng)IOT

2019-01-21 13:56:52

2022-11-15 11:13:10

域名Linux文件

2022-12-12 08:17:29

2016-11-15 09:43:56

大數(shù)據(jù)數(shù)據(jù)工程師

2023-11-06 08:41:31

JavaScript應(yīng)用程序
點贊
收藏

51CTO技術(shù)棧公眾號