变量声明形式
Go 语言沿袭了静态编译型语言的传统,使用变量之前需要先进行变量的声明。
var a int 32
var s string = "hello"
var i = 13
n := 17
var (
crlf = []byte("\r\n")
colonSpace = []byte(": ")
)
Gopher 在变量声明形式的选择上应尽量保持项目范围内的一致。
Go 语言有两类变量:
- 包级变量:在 pkg 级别可见的变量,如果是导出变量,也可以被视为全局变量;
- 局部变量:在函数或方法体内部声明的变量,只在内部可见。
包级变量
从声明变量时是否需要延迟初始化的角度对包级变量进行分类:
声明并同时显式初始化
var variableName = InitExpression
不带类型信息的常量表达式,会根据右值的类型来推导变量的类型。
```go
var a = 17
var b = 3.14
也可以显式为包级变量制定类型
// 第一种
var a int32 = 17
var b float32 = 3.14
// 第二种
var a = int32(17)
var b = float32(3.14)
Go 语言官方更推荐后者,因为统一了接收默认类型和显式指定类型两种声明形式。
声明但延迟初始化
声明时并不显式初始化包级变量:
var a int32
var f float32
这种形式的声明,变量的初始值为其类型的零值。
就近原则
变量声明最佳实践的一条:就近原则,即变量的声明应尽可能靠近其使用位置。
就近原则实际上是变量作用域最小化的一种实现手段。
var ErrNoCookie = errors.New("no cookie found")
func (r *Request) Cookie(name string) (*Cookie, error) {
for _, cookie := range r.Cookies() {
if cookie.Name == name {
return cookie, nil
}
}
return nil, ErrNoCookie
}
局部变量
延迟初始化
对于延迟初始化的局部变量声明,采用带有 var 关键词的声明形式:
func (r * byteRelacer) Replace(s string) string {
var buf bytes.Buffer // 延迟分配
for i := 0; i < len(s); i++ {
if j := r.index(s[i:]); j >= 0 {
buf.WriteString(r.new)
i += j + r.oldLen - 1
} else {
buf.WriteByte(s[i])
}
}
return buf.String()
}
另外一种常见的是声明 err,尤其是 defer
后接闭包函数需要使用 err 来判断退出状态。
func Foo() {
var err error
defer func() {
if err != nil {
...
}
}()
err = Bar()
...
}
短变量声明形式
短变量声明形式是局部变量最常用的声明形式,它的语法形式为:
a := 17
f := 3.14
s := "hello"
分支控制采用短变量声明
这种方式体现出就近原则,让变量的作用域最小化。
func (v *Buffers) WriteTo(w io.Writer) (n int64, err error) {
if wv, ok := w.(buffterWriter); ok {
return wv.writeBuffers(v)
}
for _, b := range *v {
nb, err := w.Write(b)
n += int64(nb)
if err != nil {
v.consume(n)
return n, err
}
}
v.consume(n)
return n, nil
}
良好的函数、方法设计讲究单一职责。如果在声明局部变量时遇到适合聚类的应用场景,也应该毫不犹豫地使用 var 块来声明多个局部变量。