跳转至

Go 语言变量

变量为具有类型的存储位置命名,其值可以在允许范围内改变。 本篇介绍 var、短变量声明、类型推导、多重赋值、零值、指针和变量生命周期。

var 声明

只声明类型时得到零值:

var count int
var name string
var ready bool

声明时也可初始化并由表达式推导类型:

var count = 10
var name = "Go"

多个同类变量可成组声明,但不要为了减少行数牺牲含义:

var (
    host = "127.0.0.1"
    port = 8080
)

短变量声明

函数体内可以使用 :=:

message := "hello"
attempts := 3

它不能用于包级声明。 左侧至少一个名称必须在当前词法块中是新变量:

value, err := strconv.Atoi("42")
value, nextErr := strconv.Atoi("43")
_ = err
_ = nextErr

第二次使用 := 合法,因为 nextErr 是当前块的新变量。

指定类型与推导类型

var small int8 = 10
inferred := 10

small 是 int8,inferred 通常是 int。 当类型属于外部协议、单位或 API 契约的一部分时,应显式写出类型;局部实现细节可让编译器推导。

多重赋值与交换

left, right := 1, 2
left, right = right, left

右侧先求值,因此不需要临时变量。 多返回值常用于“结果加错误”或映射、类型断言的“值加状态”模式。

空白标识符

_ 接收并丢弃值:

value, _ := strconv.Atoi("42")
fmt.Println(value)

这段代码可运行,但忽略解析错误通常不是好实践。 空白标识符适合明确忽略不需要且无风险的结果,不能作为系统性吞错方案。

指针变量

count := 1
pointer := &count
*pointer = 2
fmt.Println(count)

&count 取得地址,*pointer 访问指向的值。 是否分配到堆由编译器逃逸分析决定,不由 new 或 & 的表面写法简单决定。

new 与复合字面量

number := new(int)
user := &User{Name: "Alice"}

new(T) 返回指向 T 零值的 *T。 结构体通常使用 &T{...},因为它可同时表达字段初始化;不要为了“像其他语言构造对象”而滥用 new。

差异示例一:var 与 :=

var timeout time.Duration
timeout = 5 * time.Second

retries := 3

var 可用于包级、可显式类型并自然表达零值;:= 仅用于函数内,适合紧邻首次使用的局部初始化。 二者没有普遍性能优劣,选择应服务于作用域和可读性。

差异示例二:遮蔽与重新赋值

value := 10
if true {
    value := 20
    fmt.Println(value)
}
fmt.Println(value)

块内 value := 20 声明了新变量,外层仍为 10。 若希望修改外层变量,应写 value = 20;错误使用 := 是常见逻辑缺陷。

差异示例三:值参数与指针参数

func setValue(n int) {
    n = 9
}

func setPointer(n *int) {
    *n = 9
}

值参数修改副本,指针参数可修改调用者存储。 但指针不自动表示“更快”;小值直接复制通常更简单,是否使用指针应考虑修改语义、大小和 nil 状态。

包级变量边界

包级变量在包初始化时创建,生命周期贯穿进程。 不可变配置可用常量表达;可变依赖和缓存应评估并发安全、测试隔离和初始化失败处理。 不要把环境变量读取、数据库连接和请求状态都隐藏在全局变量中。

常见错误

  • 在内层使用 := 意外遮蔽 err 或结果变量。
  • 为消除“声明未使用”而随意写 _ = value,掩盖未完成代码。
  • 返回局部变量指针后误以为一定悬空;Go 会保证其生命周期,但仍需考虑共享和逃逸成本。
  • 使用包级可变变量保存请求级状态,引发数据竞争。
  • 用指针表示所有值,产生不必要的 nil 分支和别名关系。

工程实践

  • 将变量声明放在最小必要作用域,并靠近首次使用位置。
  • 名称表达单位和语义,如 timeout、sizeBytes,不要只写 x。
  • 对 err 遮蔽保持警惕,尤其在 if 初始化语句和循环中。
  • 共享可变变量必须通过锁、原子操作或明确所有权同步。
  • 让结构体零值尽可能有效,减少额外布尔状态和初始化分支。
  • 通过编译器和基准判断逃逸及性能,不凭直觉重写为指针。

变量的关键不只是声明语法,还包括作用域、所有权、共享关系和生命周期。