go語言常見坑

1.main包的唯一性

傳統(tǒng)語言中對主入口的要求是main函數(shù),如c++/java等糊肠,只需要保證這點即可擂啥,但是在go中還需要保證main包的唯一性。

如下才避,在一個main包定義如下函數(shù)

package main

import "fmt"

func func1(){
    fmt.Println("test func1")
}

然后在另一個main包的main函數(shù)中如下調(diào)用

package main

import "fmt"

func main(){
    //嘗試1-嘗試調(diào)用同目錄下另一個main包中的函數(shù)
    //func1()
}

報錯如下:

common_point\test_main1.go:63:2: undefined: func1

可以看到橱夭,兩個main包實際上是相互不可見的,對于自己來說都是唯一的桑逝。需要提下的是棘劣,實際項目中,可以同一目錄下包含多個main包楞遏,只要不相互調(diào)用茬暇,go build/run xxx指明對應(yīng)main包文件編譯運行即可,這樣處理的目的在于使項目結(jié)構(gòu)都清晰寡喝,同時兼容一個項目存在多個入口的情況糙俗。

2.如何跳出for select 循環(huán)

通常在for循環(huán)中,使用break可以跳出循環(huán)预鬓,但是注意在go語言中巧骚,for select配合時,break并不能跳出循環(huán)格二。

如下代碼:

func testSelectFor(chExit chan bool){
    for  {
        select {
        case v, ok := <-chExit:
            if !ok {
                fmt.Println("close channel 1", v)
                break
            }

            fmt.Println("ch1 val =", v)
        }

    }

    fmt.Println("exit testSelectFor")
}

如下調(diào)用:

//嘗試2 select for 跳出循環(huán)
c := make(chan bool)
go testSelectFor(c)

c <- true
c <- false
close(c)

time.Sleep(time.Duration(2) * time.Second)

運行結(jié)果如下劈彪,可以看到break無法跳出循環(huán):

...
close channel 1 false
close channel 1 false
close channel 1 false
close channel 1 false
...

為了解決這個問題,需要設(shè)置標(biāo)簽顶猜,break 標(biāo)簽或goto 便簽即可跳出循環(huán)沧奴,如下兩種方法均可。

func testSelectFor2(chExit chan bool){
    EXIT:
    for  {
        select {
        case v, ok := <-chExit:
            if !ok {
                fmt.Println("close channel 2", v)
                break EXIT//goto EXIT2
            }

            fmt.Println("ch2 val =", v)
        }
    }

    //EXIT2:
    fmt.Println("exit testSelectFor2")
}

同樣調(diào)用长窄,輸出結(jié)果如下:

ch2 val = true
ch2 val = false
close channel 2 false
exit testSelectFor2

3.如何在切片中查找

go中使用sort.searchXXX方法在排序好的切片中查找指定的方法滔吠,但是其返回結(jié)果很奇怪纲菌,返回是對應(yīng)的查找元素不存在時待插入的位置下標(biāo)(元素插入在返回下標(biāo)前)。如下調(diào)用:

    //嘗試3 search查找返回值
    s := []string{"ab", "ac", "ac", "bb", "bb", "ee"}
    fmt.Println("s=", s)

    fmt.Println(sort.SearchStrings(s, "aa"))
    fmt.Println(sort.SearchStrings(s, "ac"))
    fmt.Println(sort.SearchStrings(s, "ad"))
    fmt.Println(sort.SearchStrings(s, "ff"))

返回結(jié)果如下:

s= [ab ac ac bb bb ee]
0
1
3
6

可以看到屠凶,單獨根據(jù)返回值沒法判斷對應(yīng)元素是否存在驰后,封裝如下函數(shù):

func IsExist(s []string, t string) (int, bool) {
    iIndex := sort.SearchStrings(s, t)
    bExist := iIndex!=len(s) && s[iIndex]==t

    return iIndex, bExist
}

這里用返回的下標(biāo)取值再和原來值對比下,即可判斷對應(yīng)元素是否存在矗愧。

fmt.Println(IsExist(s, "aa"))
    fmt.Println(IsExist(s, "ac"))
    fmt.Println(IsExist(s, "ad"))
    fmt.Println(IsExist(s, "ff"))
    
    /*out
    0 false
    1 true
    3 false
    6 false*/

4.如何初始化帶嵌套結(jié)構(gòu)的結(jié)構(gòu)體

go的哲學(xué)是組合優(yōu)于繼承灶芝,使用struct嵌套即可完成組合,內(nèi)嵌的結(jié)構(gòu)體屬性就像外層結(jié)構(gòu)的屬性即可唉韭,可以直接調(diào)用夜涕;但是注意初始化外層結(jié)構(gòu)體時必須指定內(nèi)嵌結(jié)構(gòu)體名稱的結(jié)構(gòu)體初始化,如下看到s1方式報錯属愤,s2方式正確女器。

type stPeople struct {
    Gender bool
    Name string
}

type stStudent struct {
    stPeople
    Class int
}

//嘗試4 嵌套結(jié)構(gòu)的初始化表達式
//var s1 = stStudent{false, "JimWen", 3}
var s2 = stStudent{stPeople{false, "JimWen"}, 3}
fmt.Println(s2.Gender, s2.Name, s2.Class)

5.切片和數(shù)組

go中沒有特別復(fù)雜的數(shù)據(jù)結(jié)構(gòu),核心就是數(shù)組(array)和map住诸,切片(slice)是基于數(shù)組的驾胆。很多人容易混淆array和slice的關(guān)系,這里著重說下兩者的聯(lián)系和區(qū)別以及應(yīng)用場合贱呐。

先看二者的初始化方法,如下:

    //slice和數(shù)組初始化方法
    var a0 [5]int
    a1 := [5]int{}
    a2 := [5]int{1,2,3}
    a3 := [...]int{1,2,3}
    a4 := [5]int{1,2,3,4,5}
    fmt.Println(a0, a1, a2, a3, a4)

    var s0 []int
    s1 := a4[0:3]
    s2 := []int{1,2,3}
    s3 := make([]int, 2, 3)
    fmt.Println(s0==nil, s1, s2, s3)

輸出如下

[0 0 0 0 0] [0 0 0 0 0] [1 2 3 0 0] [1 2 3] [1 2 3 4 5]
true [1 2 3] [1 2 3] [0 0]

可以看到丧诺,array可以如下初始化:

1.單獨聲明長度,會自動填充0值奄薇,如 var a0 [5]int

2.也可以初始化時賦值驳阎,末尾沒有填滿的填充0值,如a2 := [5]int{1,2,3}

3.如果不指定長度還可以自動計算馁蒂,如a3 := [...]int{1,2,3}

而slice不關(guān)聯(lián)數(shù)組是即為nil(如var s0 []int)呵晚,稱為nil切片。slice要想不為空沫屡,必須和數(shù)組關(guān)聯(lián)饵隙,有如下幾種方法:

1.引用已有數(shù)組,如s1 := a4[0:3]

2.初始化表達式賦值谁鳍,會默認生成底層數(shù)組癞季,然后引用它,如s2 := []int{1,2,3}

3.使用make生成底層數(shù)組倘潜,數(shù)組填充0值绷柒,然后引用它,如s3 := make([]int, 2, 3)

除了引用數(shù)組的情況涮因,slice和array初始化很容易區(qū)別废睦,array必須指定長度(具體數(shù)字或...),而slice不指定長度养泡。

前面說了slice必須關(guān)聯(lián)數(shù)組嗜湃,實際上看下二者的內(nèi)存結(jié)構(gòu)就都明白了奈应,如下:

array在內(nèi)存中是連續(xù)的塊,而slice底層也是數(shù)組购披,只不過加了一個頭杖挣,分別包含數(shù)組起始地址、slice長度刚陡、slice容量惩妇,數(shù)組起始地址和slice長度決定了slice引用的數(shù)組范圍,slice容量決定了append操作時是否開辟新的底層數(shù)組筐乳。

如下操作:

    //slice以數(shù)組為基準(zhǔn)
    v0 := [4]int{-10, 20, 30, 40}
    s := v0[1:3]
    fmt.Println(v0, s, &v0[1], &s[0])

    //s[3] = 10 panic

    s = append(s, 80)
    fmt.Println(v0, s, &v0[1], &s[0])

    s = append(s, 90)
    fmt.Println(v0, s, &v0[1], &s[0])

結(jié)果:

[-10 20 30 40] [20 30] 0xc04200a488 0xc04200a488
[-10 20 30 80] [20 30 80] 0xc04200a488 0xc04200a488
[-10 20 30 80] [20 30 80 90] 0xc04200a488 0xc042008330

一開始s指向v0的第一個元素歌殃,包含兩個元素,slice長度為2蝙云,slice容量剩余數(shù)組元素長度3氓皱。打印結(jié)果可以看到,此時s-0和v0-1是同一個元素勃刨,即slice是指向已有array的波材。

然后append一個80,此時可以看到底層數(shù)組也發(fā)生了改變身隐,這是因為slice的容量為3各聘,當(dāng)前數(shù)組還夠用,所以直接用了當(dāng)前數(shù)組抡医,此時s-0和v0-1是同一個元素,即slice還是指向已有array的早敬。

然后再append一個90忌傻,此時可以看到此時s-0和v0-1不再是同一個元素,即slice指向了新的底層數(shù)組搞监,因為原有數(shù)組已經(jīng)不夠用了水孩,所以新生成數(shù)組。

一定要注意此處的邏輯琐驴,否則很容易不小心修改了底層數(shù)組或想修改底層數(shù)組而生成了新數(shù)組俘种。

那么為什么要同時又數(shù)組和切片呢,在我看來绝淡,數(shù)組是為了提供一種底層C數(shù)組的能力宙刘,而切片相當(dāng)于一種容器迭代器,可以很方便的實現(xiàn)動態(tài)語言的易操作特性牢酵,同時結(jié)合二者可以實現(xiàn)高性能和易用性兼得悬包。

明白了這些,看下如下問題馍乙,如下給函數(shù)changeValue1傳遞一個數(shù)組布近,修改數(shù)組元素值垫释,然后打印,發(fā)現(xiàn)并沒有變化撑瞧。實際上go中變量賦值都是拷貝棵譬,要想實現(xiàn)改變可以使用數(shù)組指針changeValue2或切片changeValue3,他們依然是拷貝预伺,只不過拷貝的是地址订咸,但是指向的依然是底層數(shù)組,所以能夠成功改變數(shù)組元素值扭屁,這樣就保證了go中邏輯的一致性算谈。

//數(shù)組
func changeValue1(v [5]int)  {
    v[0] = 100
}

//數(shù)組指針
func changeValue2(v *[5]int)  {
    (*v)[0] = 100
}

//切片
func changeValue3(v []int)  {
    v[0] = 100
}

// 傳遞數(shù)組參數(shù)
v1 := [5]int{-10, 20, 30, 40, 50}
changeValue1(v1)

v2 := [5]int{-10, 20, 30, 40, 50}
changeValue2(&v2)

v3 := [5]int{-10, 20, 30, 40, 50}
changeValue3(v3[0:])

fmt.Println(v1, v2, v3)

//輸出
[-10 20 30 40 50] [100 20 30 40 50] [100 20 30 40 50]

同樣需要注意的是for range遍歷切片的時候,返回的是拷貝料滥,要想改變對應(yīng)的元素值必須使用索引來改變原來的值然眼,如下:

    //for range 對數(shù)組/切片為拷貝
    v4 := [5]int{-10, 20, 30, 40, 50}
    for _,v := range v4{
        v = v*2 //不會改變原來值
    }
    for k,v := range v4{
        fmt.Println(k,v)
    }

    for k,v := range v4{
        v4[k] = v*2 //改變原來值
    }
    for k,v := range v4{
        fmt.Println(k,v)
    }

5.map結(jié)構(gòu)查找是否存在鍵值

在其他語言中,鍵值不存在直接應(yīng)用會報異常葵腹,但是在go語言中會返回一個0值高每,因此可以如下兩種方法判斷鍵值是否存在:

package main

import "fmt"

func main(){
    m := map[int]string{1:"aaa", 2:"bbb", 3:"ccc", 4:"ddd", 5:"eee"}
    fmt.Println(m)

    v0, exist0 := m[5]
    if exist0{
        fmt.Println("exist key 5", v0)
    } else{
        fmt.Println("not exist key 5")
    }

    v1 := m[5]
    if v1!=""{
        fmt.Println("exist key 5", v0)
    } else{
        fmt.Println("not exist key 5")
    }
}

演示代碼下載鏈接

文章來源:http://blog.csdn.net/wenzhou1219
感謝作者:wenzhou1219

添加小編微信:grey0805,加入知識學(xué)習(xí)小分隊~践宴!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鲸匿,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子阻肩,更是在濱河造成了極大的恐慌带欢,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烤惊,死亡現(xiàn)場離奇詭異乔煞,居然都是意外死亡,警方通過查閱死者的電腦和手機柒室,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進店門渡贾,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人雄右,你說我怎么就攤上這事空骚。” “怎么了擂仍?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵囤屹,是天一觀的道長。 經(jīng)常有香客問我防楷,道長牺丙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮冲簿,結(jié)果婚禮上粟判,老公的妹妹穿的比我還像新娘。我一直安慰自己峦剔,他們只是感情好芋哭,可當(dāng)我...
    茶點故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布买猖。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪桩卵。 梳的紋絲不亂的頭發(fā)上怯疤,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天讹语,我揣著相機與錄音售担,去河邊找鬼。 笑死辫愉,一個胖子當(dāng)著我的面吹牛栅受,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播恭朗,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼屏镊,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了痰腮?” 一聲冷哼從身側(cè)響起而芥,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎膀值,沒想到半個月后棍丐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡沧踏,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年骄酗,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片悦冀。...
    茶點故事閱讀 39,711評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖睛琳,靈堂內(nèi)的尸體忽然破棺而出盒蟆,到底是詐尸還是另有隱情,我是刑警寧澤师骗,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布历等,位于F島的核電站,受9級特大地震影響辟癌,放射性物質(zhì)發(fā)生泄漏寒屯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望寡夹。 院中可真熱鬧处面,春花似錦、人聲如沸菩掏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽智绸。三九已至野揪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瞧栗,已是汗流浹背斯稳。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留迹恐,地道東北人挣惰。 一個月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像系草,于是被迫代替她去往敵國和親通熄。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,611評論 2 353

推薦閱讀更多精彩內(nèi)容