偷偷摘套内射激情视频,久久精品99国产国产精,中文字幕无线乱码人妻,中文在线中文a,性爽19p

你有犯過(guò)這些 Go 編碼錯(cuò)誤嗎?

開(kāi)發(fā) 前端
實(shí)際上在 Go 中,所有的函數(shù)傳遞都是值傳遞。也就是將數(shù)組傳遞給函數(shù)時(shí),會(huì)復(fù)制該數(shù)組。如果真的是需要傳進(jìn)函數(shù)內(nèi)修改,可以改用切片。

大家好,我是煎魚(yú)。

在用 Go 編程時(shí),總會(huì)遇到各種奇奇怪怪的錯(cuò)誤,國(guó)內(nèi)外已經(jīng)有許多小伙伴總結(jié)過(guò)(參考鏈接見(jiàn)參考),感覺(jué)都能湊一桌了。

希望對(duì)大家有所幫助。

Go 常見(jiàn)錯(cuò)誤

1. nil Map

問(wèn)題

在程序中聲明(定義)了一個(gè) map,然后直接寫(xiě)入數(shù)據(jù)。如下代碼:

func main() {
var m map[string]string
m["煎魚(yú)"] = "進(jìn)腦子了"
}

輸出結(jié)果:

panic: assignment to entry in nil map

會(huì)直接拋出一個(gè) panic。

解決方法

解決方法其實(shí)就是要聲明并初始化,Go 里標(biāo)準(zhǔn)寫(xiě)法是調(diào)用 make 函數(shù)就可以了。如下代碼:

func main() {
m := make(map[string]string)
m["煎魚(yú)"] = "下班了"
}

這個(gè)問(wèn)題在初學(xué) Go 時(shí)是最容易踩到的錯(cuò)誤。

2. 空指針的引用

問(wèn)題

我們?cè)?Go 經(jīng)常會(huì)利用結(jié)構(gòu)體去聲明一系列的方法,他看起來(lái)向面向?qū)ο笾械?”類“,在業(yè)務(wù)代碼中非常常見(jiàn)。

如下代碼:

type Point struct {
X, Y float64
}

func (p *Point) Abs() float64 {
return math.Sqrt(p.X*p.X + p.Y*p.Y)
}

func main() {
var p *Point
fmt.Println(p.Abs())
}

這段程序能夠正常運(yùn)行嗎?正常計(jì)算和輸出?

輸出結(jié)果:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x10a3143]

goroutine 1 [running]:
main.(*Point).Abs(...)
/Users/eddycjy/awesomeProject/main.go:13
main.main()
/Users/eddycj/awesomeProject/main.go:18 +0x23

直接就恐慌了,由于空指針的引用。

解決方法

如果變量 p 是一個(gè)指針,則必須要進(jìn)行初始化才可以進(jìn)行調(diào)用。如下代碼:

func main() {
var p *Point = new(Point)
fmt.Println(p.Abs())
}

又或是用值對(duì)象的方法來(lái)解決:

func main() {
var p Point // has zero value Point{X:0, Y:0}
fmt.Println(p.Abs())
}

3. 使用對(duì)循環(huán)迭代器變量的引用

問(wèn)題

在 Go 中,循環(huán)迭代器變量是一個(gè)單一的變量,在每個(gè)循環(huán)迭代中取不同的值。這如果使用不當(dāng),可能會(huì)導(dǎo)致非預(yù)期的行為。

如下代碼:

func main() {
var out []*int
for i := 0; i < 3; i++ {
out = append(out, &i)
}
fmt.Println("Values:", *out[0], *out[1], *out[2])
fmt.Println("Addresses:", out[0], out[1], out[2])
}

輸出結(jié)果是什么。大膽猜想值是 1,2,3,地址都是不一樣的。對(duì)嗎?

輸出結(jié)果:

Values: 3 3 3
Addresses: 0x40e020 0x40e020 0x40e020

值都是 3,地址都是同一個(gè)指向。

解決方法

其中一種解決方法是將循環(huán)變量復(fù)制到一個(gè)新變量中:

 for i := 0; i < 3; i++ {
i := i // Copy i into a new variable.
out = append(out, &i)
}

輸出結(jié)果:

Values: 0 1 2
Addresses: 0x40e020 0x40e024 0x40e028

原因是:在每次迭代中,我們將 i 的地址追加到 out 切片中,但由于它是同一個(gè)變量,我們實(shí)際上追加的是相同的地址,該地址最終包含分配給 i 的最后一個(gè)值。

所以只需要拷貝一份,讓兩者脫離關(guān)聯(lián)就可以了。

4. 在循環(huán)迭代器變量上使用 goroutine

問(wèn)題

在 Go 中進(jìn)行循環(huán)時(shí),我們經(jīng)常會(huì)使用 goroutine 來(lái)并發(fā)處理數(shù)據(jù)。最經(jīng)典的就是會(huì)結(jié)合閉包來(lái)編寫(xiě)業(yè)務(wù)邏輯。

如下代碼:

values := []int{1, 2, 3, 4, 5}
for _, val := range values {
go func() {
fmt.Println(val)
}()
}

time.Sleep(time.Second)

但在實(shí)際的運(yùn)行中,上述 for 循環(huán)可能無(wú)法達(dá)到您的預(yù)期,你想的可能是順序輸出切片中的值。

輸出的結(jié)果是:

5
5
4
5
5

你可能會(huì)看到每次迭代打印的最后一個(gè)元素,甚至你會(huì)發(fā)現(xiàn),每次輸出的結(jié)果還不一樣...

如果去掉休眠代碼,會(huì)發(fā)現(xiàn) goroutine 可能根本不會(huì)開(kāi)始執(zhí)行,程序就結(jié)束了。

解決方法

這其實(shí)就是閉包使用上的一個(gè)常見(jiàn)問(wèn)題,編寫(xiě)該閉包循環(huán)的正確方法是:

values := []int{1, 2, 3, 4, 5}
for _, val := range values {
go func(val int) {
fmt.Println(val)
}(val)
}

通過(guò)將 val 作為參數(shù)添加到閉包中,在每次循環(huán)時(shí),變量 val 都會(huì)被存儲(chǔ)在 goroutine 的堆棧中,以確保最終 goroutine 執(zhí)行時(shí)值是對(duì)的。

當(dāng)然,這里還有一個(gè)隱性問(wèn)題。大家總會(huì)以為是按順序輸出 1, 2, 3, 4, 5。其實(shí)不然,因?yàn)?goroutine 的執(zhí)行是具有隨機(jī)性的,沒(méi)法確保順序。

注:經(jīng)常會(huì)變形出現(xiàn)在許多 Go 的面試題當(dāng)中,一旦復(fù)雜起來(lái)就容易讓人迷惑。

5. 數(shù)組不會(huì)被改變

問(wèn)題

切片和數(shù)字是我們?cè)?Go 程序中應(yīng)用最廣泛的數(shù)據(jù)類型,但他常常會(huì)有一些奇奇怪怪的問(wèn)題。

如下代碼:

func Foo(a [2]int) {
a[0] = 8
}

func main() {
a := [2]int{1, 2}
Foo(a)
fmt.Println(a)
}

輸出結(jié)是什么。是 [8 2],對(duì)嗎?

輸出結(jié)果:

[1 2]

這是為什么,函數(shù)里修改了個(gè)寂寞?

解決方法

實(shí)際上在 Go 中,所有的函數(shù)傳遞都是值傳遞。也就是將數(shù)組傳遞給函數(shù)時(shí),會(huì)復(fù)制該數(shù)組。如果真的是需要傳進(jìn)函數(shù)內(nèi)修改,可以改用切片。

如下代碼:

func Foo(a []int) {
if len(a) > 0 {
a[0] = 8
}
}

func main() {
a := []int{1, 2}
Foo(a)
fmt.Println(a)
}

輸出結(jié)果:

[8 2]

原因是:切片不會(huì)存儲(chǔ)任何的數(shù)據(jù),他的底層 data 會(huì)指向一個(gè)底層數(shù)組。因此在修改切片的元素時(shí),會(huì)修改其底層數(shù)組的相應(yīng)元素,共享同一個(gè)底層數(shù)組的其他切片會(huì)一并修改。

你以為這就萬(wàn)事大吉,解決了?并不。當(dāng)切片擴(kuò)容時(shí),Go 底層會(huì)重新申請(qǐng)新的更大空間,存在與原有切片分離的場(chǎng)景。

因此還是要及時(shí)將變更的值返回出來(lái),在主流程上統(tǒng)一處理元數(shù)據(jù)會(huì)更好。

總結(jié)

在今天這篇文章中,我們開(kāi)始了 Go 常見(jiàn)編碼錯(cuò)誤的第一節(jié),共涉及 5 個(gè)案例:

  • nil Map。
  • 空指針的引用。
  • 使用對(duì)循環(huán)迭代器變量的引用。
  • 在循環(huán)迭代器變量上使用 goroutine。
  • 數(shù)組不會(huì)被改變。

這些案例非常常見(jiàn),在單一代碼上看會(huì)比較容易發(fā)覺(jué)。但一旦混合到應(yīng)用程序中,在繁雜代碼里就比較難看出來(lái)。

祝大家吸完后少踩坑,少出 BUG。

參考

golang/go/wiki/CommonMistakes

24 Common Mistakes in Go (gotchas) And How To Avoid Them

責(zé)任編輯:武曉燕 來(lái)源: 腦子進(jìn)煎魚(yú)了
相關(guān)推薦

2022-10-17 07:40:21

AI項(xiàng)目數(shù)據(jù)

2011-06-29 09:27:10

2019-10-08 09:00:00

MySQL數(shù)據(jù)庫(kù)

2021-11-10 15:37:49

Go源碼指令

2021-10-28 19:21:56

GolangGo變量

2023-03-13 13:36:00

Go擴(kuò)容切片

2021-12-16 06:52:33

Ceph分布式對(duì)象

2018-07-06 15:00:50

碼農(nóng)科技開(kāi)發(fā)

2019-09-24 21:00:59

SQL數(shù)據(jù)庫(kù)基礎(chǔ)數(shù)據(jù)庫(kù)

2016-03-28 13:39:58

戴爾

2018-04-08 22:16:21

2019-12-30 09:28:53

Kafka集群ZooKeeper

2021-11-29 09:45:57

枚舉Go代碼

2020-03-23 08:15:43

JavaScriptError對(duì)象函數(shù)

2010-08-17 09:08:16

數(shù)據(jù)中心錯(cuò)誤

2011-08-11 16:56:45

數(shù)據(jù)挖掘

2020-10-28 11:20:55

vue項(xiàng)目技

2023-12-07 08:13:58

Java開(kāi)發(fā)

2022-07-19 07:30:06

BigDecimal運(yùn)算float

2019-01-23 17:53:05

程序員技能溝通
點(diǎn)贊
收藏

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