go变量
动态?静态?
动态语言(比如 Python、Ruby 等):解释器可以在运行时通过对变量赋值的分析,自动确定变量的边界。并且在动态语言中,一个变量可以在运行时被赋予大小不同的边界。
静态语言(比如Go):静态类型语言编译器必须明确知道一个变量的边界才允许使用这个变量,但静态语言编译器又没能力自动提供这个信息,这个边界信息必须由这门语言的使用者提供,于是就有了“变量声明”。通过变量声明,语言使用者可以显式告知编译器一个变量的边界信息。在具体实现层面呢,这个边界信息由变量的类型属性赋予。
变量声明
单变量声明
基本用法
如果没有显示定义初值,go编译器会为这个变量设置这个类型的零值
var a int // a的初值为int类型的零值:0
Go 语言的每种原生类型都有它的默认值(这些原生类型我们后面再讲),这个默认值就是这个类型的零值。这里我给你写了 Go 规范定义的内置原生类型的默认值(即零值):
多变量一起声明
var a, b, c int = 5, 6, 7
变量声明块(block)
var ( a int = 128 b int8 = 6 s string = "hello" c rune = 'A' t bool = true )
var ( a, b, c int = 5, 6, 7 c, d, e rune = 'C', 'D', 'E' )
省略写法
1.省略类型信息的声明
var b = 13
Go 编译器会根据右侧变量初值自动推导出变量的类型,并给这个变量赋予初值所对应的默认类型。比如,整型值的默认类型 int,浮点值的默认类型为 float64,复数值的默认类型为 complex128。其他类型值的默认类型就更好分辨了,在 Go 语言中仅有唯一与之对应的类型,比如布尔值的默认类型只能是 bool,字符值默认类型只能是 rune,字符串值的默认类型只能是 string 等。
如果我们不接受默认类型,而是要显式地为变量指定类型,除了通用的声明形式,我们还可以通过显式类型转型达到我们的目的:
var b = int32(13)
多个不同类型的变量声明
var a, b, c = 12, 'A', "hello"
2.短变量声明
可以省去 var 关键字以及类型信息,它的标准范式是“varName := initExpression”。例子:
a := 12 b := 'A' c := "hello"
a, b, c := 12, 'A', "hello"
不过,短变量声明的使用也是有约束的,并不是所有变量都能用短变量声明来声明的,这个你会在下面的讲解中了解到。
变量分类
包级变量(package varible)
在包级别可见的变量。如果是导出变量(大写字母开头),那么这个包级变量也可以被视为全局变量;
声明形式
包级变量只能使用带有 var 关键字的变量声明形式,不能使用短变量声明形式,但在形式细节上可以有一定灵活度。
第一类:声明并同时显式初始化
先上代码
// $GOROOT/src/io/io.go var ErrShortWrite = errors.New("short write") var ErrShortBuffer = errors.New("short buffer") var EOF = errors.New("EOF")
我们可以看到,这个代码块里声明的变量都是 io 包的包级变量。在 Go 标准库中,对于变量声明的同时进行显式初始化的这类包级变量,实践中多使用这种省略类型信息的“语法糖”格式:
//第一种: plain var a = 13 // 使用默认类型 var b int32 = 17 // 显式指定类型 var f float32 = 3.14 // 显式指定类型 //第二种: var a = 13 // 使用默认类型 var b = int32(17) // 显式指定类型 var f = float32(3.14) // 显式指定类型
虽然这两种方式都是可以使用的,但从声明一致性的角度出发,Go 更推荐我们使用后者,这样能统一接受默认类型和显式指定类型这两种声明形式,尤其是在将这些变量放在一个 var 块中声明时,你会更明显地看到这一点。
所以我们更青睐下面这样的形式:
var ( a = 13 b = int32(17) f = float32(3.14) )
第二类:声明但延迟初始化
对于声明时并不立即显式初始化的包级变量,我们可以使用下面这种通用变量声明形式:
var a int32 var f float64
我们知道,虽然没有显式初始化,Go 语言也会让这些变量拥有初始的“零值”。如果是自定义的类型,我也建议你尽量保证它的零值是可用的。
这里还有一个注意事项,就是声明聚类与就近原则。
Go 语言提供了变量声明块用来把多个的变量声明放在一起,并且在语法上也不会限制放置在 var 块中的声明类型,那我们就应该学会充分利用 var 变量声明块,让我们变量声明更规整,更具可读性,现在我们就来试试看。通常,我们会将同一类的变量声明放在一个 var 变量声明块中,不同类的声明放在不同的 var 声明块中,比如下面就是我从标准库 net 包中摘取的两段变量声明代码:
// $GOROOT/src/net/net.go var ( netGo bool netCgo bool ) var ( aLongTimeAgo = time.Unix(1, 0) noDeadline = time.Time{} noCancel = (chan struct{})(nil) )
局部变量(local varible)
Go 函数或方法体内声明的变量,仅在函数或方法体内可见。
第一类:对于延迟初始化的局部变量声明,我们采用通用的变量声明形式
var err error
第二类:对于声明且显式初始化的局部变量,建议使用短变量声明形式
短变量声明形式是局部变量最常用的声明形式,它遍布在 Go 标准库代码中。对于接受默认类型的变量,我们使用下面这种形式:
a := 17 f := 3.14 s := "hello, gopher!"
对于不接受默认类型的变量,我们依然可以使用短变量声明形式,只是在":="右侧要做一个显式转型,以保持声明的一致性:
a := int32(17) f := float32(3.14) s := []byte("hello, gopher!")