Golang中常用的語法糖分享
1、名字由來
語法糖(Syntactic sugar)的概念是由英國計算機(jī)科學(xué)家彼得·蘭丁提出的,用于表示編程語言中的某種類型的語法,這些語法不會影響功能,但使用起來卻很方便。
語法糖,也稱糖語法,這些語法不僅不會影響功能,編譯后的結(jié)果跟不使用語法糖也一樣。
語法糖,有可能讓代碼編寫變得簡單,也有可能讓代碼可讀性更高,但有時也會給你一個意外,也可能帶你掉入陷阱讓您的代碼出問題。本文將講解Golang常用語法糖。
2、Golang常用語法糖
2.1 簡短變量聲明 :=
規(guī)則:簡短變量聲明符這個語法糖使用起來很方便,導(dǎo)致你可能隨手就會使用它定義一個變量,往往程序的bug就是隨手寫出來的,在這里說一下簡短變量聲明的原理和規(guī)則。
(1)多變量賦值可能會重新聲明
使用 := 一次可以聲明多個變量,例如:
i, j := 0, 0 j, k := 1, 1
當(dāng) := 左側(cè)存在新的變量時(如 k),那么已經(jīng)聲明的變量(如 j)會被重新聲明。這并沒有引入新的變量,只是把變量的值改變了。
當(dāng) := 左側(cè)沒有新變量編譯報錯。如下示例由于左側(cè)沒有新變量編譯會提示" No new variables on the left side of ':=' "錯誤。
i,j := 2,3 i,j := 6,8
(2)不能用于函數(shù)外部
:= 這種簡短變量聲明只能用于函數(shù)中,用來初始化全局變量是不行的。
可以理解成 := 會拆分成兩個語句,即聲明和賦值。賦值語句不能出現(xiàn)在函數(shù)外部的,因為在任何函數(shù)外,語句都應(yīng)該以關(guān)鍵字開頭,例如 type、var這樣的關(guān)鍵字。
比如,像下面這樣:
package sugar import fmt rule := "Short variable declarations" // syntax error: non-declaration statement outside function body
這是因為在函數(shù)外部聲明的變量是全局變量,它們具有包級別的作用域。在包級別作用域中,變量的聲明通常是顯式的,不需要使用短變量聲明語法糖。而且在全局變量的聲明中,必須指定變量的類型,這是因為編譯器需要知道變量的大小和布局信息,以便在編譯時為它們分配內(nèi)存。
因此,如果要在包級別聲明變量,需要使用 var 關(guān)鍵字或 const 關(guān)鍵字進(jìn)行顯式聲明,不能使用 := 語法糖。例如:
package main import "fmt" // 使用 var 關(guān)鍵字顯式聲明全局變量 var globalVar = 10 func main() { // 在函數(shù)內(nèi)部使用 := 語法糖聲明局部變量 localVar := 20 fmt.Println(globalVar, localVar) }
總之,:= 只能用于局部變量的聲明和初始化,而不能用于全局變量的聲明和初始化,這是 Go 語言的語法規(guī)定。
(3)變量作用域問題
幾乎所有的工程師都了解變量作用域,但是由于:=使用過于頻繁的話,還是有可能掉進(jìn)陷阱里。
下面代碼源自真實項目,但為了描述方便,也為了避免信息安全風(fēng)險,簡化如下:
func Redeclare() { field, err:= nextField() // 1號err if field == 1{ field, err:= nextField() // 2號err newField, err := nextField() // 3號err ... } ... }
注意上面聲明的三個err變量。 2號err與1號err不屬于同一個作用域,:=聲明了新的變量,所以2號err與1號err屬于兩個變量。 2號err與3號err屬于同一個作用域,:=重新聲明了err但沒創(chuàng)建新的變量,所以2號err與3號err是同一個變量。(同一變量重復(fù)賦值會重新聲明,這并沒有引入新的變量,只是把變量的值改變了。)
如果誤把2號err與1號err混淆,就很容易產(chǎn)生意想不到的錯誤。
2.2 可變參函數(shù) ...
我們先寫一個可變參函數(shù):
func Greeting(prefix string, who ...string) { if who == nil { fmt.Printf("Nobody to say hi.") return } for _, people := range who{ fmt.Printf("%s %s\n", prefix, people) } }
Greeting函數(shù)負(fù)責(zé)給指定的人打招呼,其參數(shù)who為可變參數(shù)。這個函數(shù)幾乎把可變參函數(shù)的特征全部表現(xiàn)出來了:
可變參數(shù)必須在函數(shù)參數(shù)列表的最后一個(否則會引起編譯時歧義);
可變參數(shù)在函數(shù)內(nèi)部是作為切片來解析的;
可變參數(shù)可以不填,不填時函數(shù)內(nèi)部當(dāng)成 nil 切片處理;
可變參數(shù)可以填入切片;
可變參數(shù)必須是相同類型的(如果需要是不同類型的可以定義為 interface{}類型);
(1)使用舉例-不傳值
調(diào)用可變參函數(shù)時,可變參部分是可以不傳值的,例如:
func ExampleGreetingWithoutParameter() { sugar.Greeting("nobody") // OutPut: // Nobody to say hi. }
這里沒有傳遞第二個參數(shù)??勺儏?shù)不傳遞的話,默認(rèn)為nil。
(2)使用舉例-傳遞多個參數(shù)
調(diào)用可變參函數(shù)時,可變參數(shù)部分可以傳遞多個值,例如:
func ExampleGreetingWithParameter() { sugar.Greeting("hello:", "Joe", "Anna", "Eileen") // OutPut: // hello: Joe // hello: Anna // hello: Eileen }
可變參數(shù)可以有多個。多個參數(shù)將會生成一個切片傳入,函數(shù)內(nèi)部按照切片來處理。
(3)使用舉例-傳遞切片
調(diào)用可變參函數(shù)時,可變參數(shù)部分可以直接傳遞一個切片。參數(shù)部分需要使用slice...來表示切片。例如:
func ExampleGreetingWithSlice() { guest := []string{"Joe", "Anna", "Eileen"} sugar.Greeting("hello:", guest...) // OutPut: // hello: Joe // hello: Anna // hello: Eileen }
此時需要注意的一點(diǎn)是,切片傳入時不會生成新的切片,也就是說函數(shù)內(nèi)部使用的切片與傳入的切片共享相同的存儲空間。說得再直白一點(diǎn)就是,如果函數(shù)內(nèi)部修改了切片,可能會影響外部調(diào)用的函數(shù)。
2.3 new函數(shù)
在 Go 語言中,new 函數(shù)用于動態(tài)地分配內(nèi)存,返回一個指向新分配的零值的指針。它的語法如下:
func new(Type) *Type
其中,Type 表示要分配的內(nèi)存的類型,new 函數(shù)返回一個指向 Type 類型的新分配的零值的指針。但是需要注意的是,new 函數(shù)只分配內(nèi)存,并返回指向新分配的零值的指針,而不會初始化該內(nèi)存。
所謂零值,是指 Go 語言中變量在聲明時自動賦予的默認(rèn)值。對于基本類型來說,它們的零值如下:
- 布爾型:false
- 整型:0
- 浮點(diǎn)型:0.0
- 復(fù)數(shù)型:0 + 0i
- 字符串:""(空字符串)
- 指針:nil
- 接口:nil
- 切片、映射和通道:nil
因此,new 函數(shù)返回的指針指向新分配的零值,但不會將其初始化為非零值。如果需要將內(nèi)存初始化為非零值,可以使用結(jié)構(gòu)體字面量或者顯式地為其賦值。例如:
package main import "fmt" type Person struct { name string age int sex int } func main() { // 使用 new 函數(shù)分配內(nèi)存,但不會將其初始化為非零值 p := new(Person) fmt.Println(p) // 輸出:&{ 0 0} // 使用結(jié)構(gòu)體字面量初始化 p2 := &Person{name: "Tom", age: 18, sex: 1} fmt.Println(p2) // 輸出:&{Tom 18 1} // 顯式為字段賦值 p3 := new(Person) p3.name = "Jerry" p3.age = 20 p3.sex = 0 fmt.Println(p3) // 輸出:&{Jerry 20 0} }
上面的代碼中,使用 new 函數(shù)分配了一個新的 Person 結(jié)構(gòu)體,但不會將其初始化為非零值,因此輸出結(jié)果是"空字符串 0 0"。接下來,使用結(jié)構(gòu)體字面量或者顯式為其賦值,將其初始化為非零值?! ?/p>
很明顯,new函數(shù)的設(shè)計同樣是為了方便程序員的使用。
注意 1:p3 := new(Person) 返回是指向新分配的Person類型對象零值的指針,按照我們對指針語法的了解,基于p3顯示賦值的話需要使用如下語法進(jìn)行賦值:
(*p3).name = "Jerry" (*p3).age = 20 (*p3).sex = 0
而我們在對指針類型結(jié)構(gòu)體對象賦值的時候一般都很少會帶著*,這也是Go指針語法糖為我們做的簡化,這部分在后文會詳細(xì)介紹?! ?/p>
注意 2:new函數(shù)更多細(xì)節(jié)介紹,請參見《Go語言new( )函數(shù)》這篇博文。
到此這篇關(guān)于Golang中常用的語法糖分享的文章就介紹到這了,更多相關(guān)Golang語法糖內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
go語言goto語句跳轉(zhuǎn)到指定的標(biāo)簽實現(xiàn)方法
這篇文章主要介紹了go語言goto語句跳轉(zhuǎn)到指定的標(biāo)簽實現(xiàn)方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-05-05Go語言模型:string的底層數(shù)據(jù)結(jié)構(gòu)與高效操作詳解
這篇文章主要介紹了Go語言模型:string的底層數(shù)據(jù)結(jié)構(gòu)與高效操作詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-12-12Go中Gzip與json搭配實現(xiàn)數(shù)據(jù)壓縮demo
這篇文章主要為大家介紹了Go中Gzip與json搭配使用壓縮數(shù)據(jù)的實現(xiàn)demo,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05Go語言編程通過dwarf獲取內(nèi)聯(lián)函數(shù)
這篇文章主要為大家介紹了Go語言編程通過dwarf獲取內(nèi)聯(lián)函數(shù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-11-11Golang實現(xiàn)斷點(diǎn)續(xù)傳功能
這篇文章主要為大家詳細(xì)介紹了Golang實現(xiàn)斷點(diǎn)續(xù)傳、復(fù)制文件功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-07-07