Go习惯用法(多值赋值短变量声明赋值简写模式)基础实例
作者:程序员的自我进化
1. 多值赋值
可以一次性声明多个变量,并可以在声明时赋值,而且可以省略类型,但必须遵守一定的规则要求。
package main import "fmt" func main() { var x, y int // 相同类型变量可以在末尾带上类型 var a, b int = 1, 2 var c, d = 3, 4 // 不带类型时,编译器自动推断 var ( // 不同类型变量声明和隐式初始化 e int f 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 = 0 var b int = 0 a, b := 1, 2 fmt.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 bool downStatus 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 <- 1 c <- 2 close(c) }() for { v, ok := <- c if ok { Println("v exist") } else { Println("v not exist") } } for v := range c { Println(v) }
类型断言
如果 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 main import "fmt" func main() { var x, y int = 3, 5 fmt.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 int tmp = x x = y y = tmp return x, y } func swap_reference(x *int, y *int) (int, int) { var tmp int tmp = *x *x = *y *y = tmp return *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习惯用法(多值赋值短变量声明赋值简写模式)基础实例的详细内容,更多关于Go多值赋值短变量声明的资料请关注脚本之家其它相关文章!