您好,登錄后才能下訂單哦!
前言
相信用過Range的朋友們都知道,Go語言中的range關鍵字使用起來非常的方便,它允許你遍歷某個slice或者map,并通過兩個參數(index
和value
),分別獲取到slice或者map中某個元素所在的index
以及其值。
比如像這樣的用法:
for index, value := range mySlice { fmt.Println("index: " + index) fmt.Println("value: " + value) }
上面的例子足夠清晰的描述了range的用法,實際上在使用range關鍵字的時候,還有一些需要特別注意的地方,有一些新手很容易入的”坑”。
為了說明這些”坑”,我們可以從下面這個稍復雜的例子說起:
type Foo struct { bar string } func main() { list := []Foo{ {"A"}, {"B"}, {"C"}, } list2 := make([]*Foo, len(list)) for i, value := range list { list2[i] = &value } fmt.Println(list[0], list[1], list[2]) fmt.Println(list2[0], list2[1], list2[2]) }
在這個例子中,我們干了下面的一些事情:
1、定義了一個叫做Foo的結構,里面有一個叫bar的field。隨后,我們創建了一個基于Foo結構體的slice,名字叫list
2、我們還創建了一個基于Foo結構體指針類型的slice,叫做list2
3、在一個for
循環中,我們試圖遍歷list中的每一個元素,獲取其指針地址,并賦值到list2中index與之對應的位置。
4、最后,分別輸出list與list2中的每個元素
從代碼來看,理所當然,我們期望得到的結果應該是這樣:
{A} {B} {C} &{A} &{B} &{C}
但是結果卻出乎意料,程序的輸出是這樣的:
{A} {B} {C} &{C} &{C} &{C}
從結果來看,仿佛list2中的三個元素,都指向了list中的最后一個元素。這是為什么呢?問題就出在上面那一段for…range
循環中。
在Go的for…range
循環中,Go始終使用值拷貝的方式代替被遍歷的元素本身,簡單來說,就是for…range
中那個value
,是一個值拷貝,而不是元素本身。這樣一來,當我們期望用&獲取元素的指針地址時,實際上只是取到了value
這個臨時變量的指針地址,而非list
中真正被遍歷到的某個元素的指針地址。而在整個for…range
循環中,value
這個臨時變量會被重復使用,所以,在上面的例子中,list2被填充了三個相同的指針地址,并且這三個地址都指向value
,而在最后一次循環中,value
被賦與了{c}
的指針地址。因此,list2輸出的時候顯示出了三個&{c}
。
同樣的,下面的寫法,跟for…range
的例子如出一轍:
var value Foo for var i := 0; i < len(list); i++ { value = list[i] list2[i] = &value }
如果我們輸出list2的三個元素,結果同樣是: &{C} &{C} &{C}
那么,怎樣才是正確的寫法呢?我們應該用index
來訪問for…range
中真實的元素,并獲取其指針地址:
for i, _ := range list { list2[i] = &list[i] }
這樣,輸出list2中的元素,就能得到我們想要的結果(&{A} &{B} &{C})
了。
實驗代碼如下:
package main import "fmt" type Foo struct { bar string } func main() { list := []Foo{ {"A"}, {"B"}, {"C"}, } list2 := make([]*Foo, len(list)) //錯誤的例子 for i, value := range list { list2[i] = &value } //正確的例子 //for i, _ := range list { // list2[i] = &list[i] //} fmt.Println(list[0], list[1], list[2]) fmt.Println(list2[0], list2[1], list2[2]) }
了解了range的正確使用姿勢,那么我們下面這個例子也能迎刃而解了:
package main import "fmt" type MyType struct { field string } func main() { var array [10]MyType for _, e := range array { e.field = "foo" } for _, e := range array { fmt.Println(e.field) fmt.Println("--") } }
平常寫代碼最常見的場景,就是我們需要在一個循環中修改被遍歷元素的值。比如上面這個例子,我們希望能使用for…range
循環,一次性將array中每個元素的field設置為”foo”。同樣,因為range值拷貝的緣故,上面的程序什么都不會輸出……
而正確的做法是:
for i, _ := range array { array[i].field = "foo" }
通過index訪問每個元素,并修改其field,這樣,就能輸出一堆”foo”了……
實驗代碼如下:
package main import "fmt" type MyType struct { field string } func main() { var array [10]MyType for i, _ := range array { array[i].field = "foo" } for _, e := range array { fmt.Println(e.field) } }
總結
以上就是關于Go語言中Range關鍵字的全部內容,這篇文章介紹的還是很詳細的,相信本文會對大家學習Go語言具有一定的參考價值,如果有疑問大家可以留言交流,小編會盡快給大家回復的,也請大家繼續支持億速云。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。