Go 学习笔记(26)— Go 习惯用法(多值赋值,短变量声明和赋值,简写模式、多值返回函数、comma,ok 表达式、传值规则)
1. 多值賦值
可以一次性聲明多個變量,并可以在聲明時賦值,而且可以省略類型,但必須遵守一定的規則要求。
package main import "fmt"func main() {var x, y int // 相同類型變量可以在末尾帶上類型var a, b int = 1, 2var c, d = 3, 4 // 不帶類型時,編譯器自動推斷var ( // 不同類型變量聲明和隱式初始化e intf string)var g int, e int = 6, 7 // 多賦值語句中每個變量后面不能都帶上類型fmt.Println("x ", x)fmt.Println("y ", y)fmt.Println("a ", a)fmt.Println("b ", b)fmt.Println("c ", c)fmt.Println("d ", d)fmt.Println("e ", e)fmt.Println("f ", f)
}
有如下錯誤:
.\main.go:14:14: syntax error: unexpected comma at end of statement
2. 短變量的聲明和賦值
Go 語言的語法允許多值短變量聲明和賦值的多個變量中,只要有一個是新變量就可以使用“:=”進行賦值。
也就是說,在多值短變量的聲明和賦值時, 至少有一個變量是新創建的局部變量,其他的變量可以復用以前的變量,不是新創建的變量執行的僅僅是賦值。
package main import "fmt"func main() {var a int = 0var b int = 0a, b := 1, 2fmt.Println("a ", a)fmt.Println("b ", b)
}
會有以下錯誤:
.\main.go:8:10: no new variables on left side of :=
要想通過編譯, a, b := 1, 2 至少一個變量是新定義的局部變量,如果在賦值語句 a, b := 1, 2 中已經存在一個局部變量 a ,則賦值語句不會創建新的變量 a , 而是使用 1 賦值給已經聲明的局部變量。但是會創建新的變量 b 并給其賦值。
賦值操作符 = 和 := 的區別:
-
=不會聲明并創建新變量,而是在當前賦值語句所在的作用域由內向外逐層去搜尋變量,如果沒有搜索到相同的變量名,則報編譯錯誤。 -
:=必須出現在函數或者類型方法內部。 -
:=至少要創建一個局部變量并初始化。
多值短變量聲明賦值 := 的最佳使用場景是在錯誤處理上。例如:
a, err := f()
if err ! = nil {// do something
}// 此時 err 可以是已存在的 err 變量,只是重新賦值了b, err := g()
3. 簡寫模式
Go 語言很多重復的引用或聲明可以用 () 進行簡寫。
- import 多個包;
// 推薦寫法
import ("os""fmt"
)// 不推薦寫法
import "os"
import "fmt"
- 多個變量聲明;
包中多個相關全局變量聲明時,建議使用 () 進行合并聲明
// 推薦寫法
var (uploadStatus booldownStatus bool
)// 不推薦寫法
var uploadStatus bool
var uploadStatus bool
4. 多值返回函數
多值返回函數里如果有 error 或 bool 類型的返回值,則應該將 error 和 bool 作為最后一個返回值。這是一種編程風格,沒有對錯。
buffer.go:107: func (b *Buffer) tryGrowByReslice(n int) (int, bool) {
buffer.go:335: func (b *Buffer) ReadByte() (byte , error) {
5. comma,ok 表達式
常見的幾個 comma, ok 表達式用法有以下幾種情況:
- 獲取
map值
獲取 map 中不存在鍵的值不會發生異常,而是會返回值類型的零值,如果想確定 map 中是否存在某個 key,則可以使用獲取 map 值的 comma, ok語法。 示例如下:
m := make(map[string] string)
v, ok := m["camera"]
if ok {Println("camera exist")
} else {Println("camera not exist")
}
- 讀取
chan值
讀取已經關閉的通道不會阻塞,也不會引起 panic, 而是一直返回該通道的零值,判斷通道關閉有兩種方法,一種是 comma, ok 表達式;另一種是通過 range 循環迭代。
c := make(chan int)
go func() {c <- 1c <- 2close(c)
}()for {v, ok := <- cif ok {Println("v exist")} else {Println("v not exist")}}for v := range c {Println(v)
}
- 類型斷言
v, ok = x.(T) // type assertion
如果 map 查找、類型斷言或通道接收出現在賦值語句的右邊,它們都可能會產生兩個結果,有一個額外的布爾結果表示操作是否成功:
v, ok = m[key] // map lookup
v, ok = x.(T) // type assertion
v, ok = <-ch // channel receive
注意: map 查找、類型斷言或通道接收出現在賦值語句的右邊時,并不一定是產生兩個結果,也可能只產生一個結果。對于只產生一個結果的情形, map 查找失敗時會返回零值,類型斷言失敗時會發生運行時 panic 異常,通道接收失敗時會返回零值(阻塞不算是失敗)。例如下面的例子:
v = m[key] // map查找,失敗時返回零值
v = x.(T) // type斷言,失敗時panic異常
v = <-ch // 管道接收,失敗時返回零值(阻塞不算是失敗)
_, ok = m[key] // map返回2個值
_, ok = mm[""], false // map返回1個值
_ = mm[""] // map返回1個值
和變量聲明一樣,我們可以用下劃線空白標識符 _ 來丟棄不需要的值。
_, err = io.Copy(dst, src) // 丟棄字節數
_, ok = x.(T) // 只檢測類型,忽略具體值
6. 傳值規則
Go 只有一種參數傳遞規則,那就是值拷貝,這種規則包括兩種含義:
-
函數參數傳遞時使用的是值拷貝。
-
實例賦值給接口變量,接口對實例的引用是值拷貝。
有時在明明是值拷貝的地方,結果卻修改了變量的內容,有以下兩種情況:
-
直接傳遞的是指針。指針傳遞同樣是值拷貝,但指針和指針副本的值指向的地址是同一個地方,所以能修改實參值。
-
參數是復合數據類型,這些復合數據類型內部有指針類型的元素,此時參數的值拷貝并不影響指針的指向。
Go 復合類型中 chan 、 map 、 slice 、 interface 內部都是通過指針指向具體的數據,這些類型的變量在作為函數參數傳遞時,實際上相當于指針的副本。
package mainimport "fmt"func main() {var x, y int = 3, 5fmt.Printf("befor swap x is %d\n", x)fmt.Printf("befor swapy is %d\n", y)x, y = swap_value(x, y)fmt.Printf("after swap x is %d\n", x)fmt.Printf("after swap y is %d\n", y)x, y = swap_reference(&x, &y)fmt.Printf("after swap_reference x is %d\n", x)fmt.Printf("after swap_reference y is %d\n", y)
}func swap_value(x int, y int) (int, int) {var tmp inttmp = xx = yy = tmpreturn x, y}func swap_reference(x *int, y *int) (int, int) {var tmp inttmp = *x*x = *y*y = tmpreturn *x, *y}
輸出:
befor swap x is 3
befor swapy is 5
after swap x is 5
after swap y is 3
after swap_reference x is 3
after swap_reference y is 5
總結
以上是生活随笔為你收集整理的Go 学习笔记(26)— Go 习惯用法(多值赋值,短变量声明和赋值,简写模式、多值返回函数、comma,ok 表达式、传值规则)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 一件正宗的羊绒衫大概多少钱
- 下一篇: 百开头的成语接龙大全