Go 变量的作用域

339 阅读3分钟

这是我参与2022首次更文挑战的第5天,活动详情查看:2022首次更文挑战」。

Go 语言的变量可以分为两类

包级变量 (package varible)

  • 也就是在包级别可见的变量
  • 如果是导出变量(大写字母开头),那么这个包级变量也可以被视为全局变量(被其他包引用)
  • 所有的函数都可以使用,而且共享这一份数据

局部变量 (local varible)

  • 也就是 Go 函数或方法体内声明的变量,仅在函数或方法体内可见
  • 变量在哪里定义,就只能在哪个范围使用,超出这个范围,我们认为变量就被销毁了

实际 🌰

package main

import "fmt"

// 包级变量
var all_sum int

func add(x int, y int) int {
	// 局部变量 sum
	sum := x + y

	// 函数体内可访问包级变量
	all_sum += sum

	return sum
}

func main() {
	fmt.Println(
		add(1, 2),
		add(3, 4),
		all_sum,
	)

}

运行结果

3 7 10

包级变量的声明方式

包级变量只能使用带有 var 关键字的变量声明形式,不能使用短变量声明形式

可以显式地为包级变量指定类型

// 第一种
var a = 13           // 使用默认类型
var b int32 = 18     // 显式指定类型
var f float32 = 3.14 // 显式指定类型

// 第二种
var a1 = 13            // 使用默认类型
var b1 = int32(32)     // 显式指定类型
var f1 = float32(3.14) // 显式指定类型

Go 推荐使用第二种写法,以及下面的形式

var (
	a2 = 13
	b2 = int32(17)
	f2 = float32(3.14)
)

声明但延迟初始化

声明包级变量但不立即显式初始化的包级变量

var a int32
var f float32
  • Go 也会让这些变量拥有初始的零值
  • 如果是自定义的类型,建议尽量保证它的零值是可用的

声明聚类

建议将同一类的变量声明放在一个 var 变量声明块中,不同类的声明放在不同的var 声明块中

// 将延迟初始化的变量声明放在这个块中
var (
	netGo  bool
	netCgo bool
)

// 将显式初始化的变量放在这个块中
var (
	aLongTimeAgo = time.Unix(1, 0)
	noDeadline   = time.Time{}
	noCancel     = (chan struct{})(nil)
)

这种就是声明聚类,提高代码可读性

就近原则

ErrNoCookie 这个变量在整个包中仅仅被用在了 Cookie 方法中,因此它被声明在紧邻 Cookie 方法定义的地方更合理

// $GOROOT/src/net/http/request.go

var ErrNoCookie = errors.New("http: named cookie not present")

func (r *Request) Cookie(name string) (*Cookie, error) {
	for _, c := range readCookies(r.Header, name) {
		return c, nil
	}
	return nil, ErrNoCookie
}

如果一个包级变量在包内部被多处使用,那么这个变量还是放在源文件头部声明比较适合的

局部变量的声明方式

// 第一种:延迟初始化的变量声明
var (
	a int
	b bool
)

// 第二种:显式声明类型或初始化赋值
var (
	var c = 13
    var d = float64(3.14)
)

// 第三种:短变量声明方式
e := 13
f := 3.14

尽量在分支控制时使用短变量声明形式

这是 strings 包的 LastIndexAny 函数

func LastIndexAny(s, chars string) int {

	...
	if len(s) > 8 {
        // 短变量声明了 as、isASCII,他们可以在 if 代码块中使用
		if as, isASCII := makeASCIISet(chars); isASCII {
			for i := len(s) - 1; i >= 0; i-- {
				if as.contains(s[i]) {
					return i
				}
			}
			return -1
		}
	}
	...
    
    // 短变量声明了 i,他可以在 for 代码块中使用
	for i := len(s); i > 0; {
		r, size := utf8.DecodeLastRuneInString(s[:i])
		i -= size
		if IndexRune(chars, r) >= 0 {
			return i
		}
	}
	return -1
}