Go 语言变量
声明、多变量、短声明、作用域与遮蔽、以及值语义。
Key PointsGo 的变量是值语义:赋值和传参都是复制,指针才共享。
声明与赋值
Go
var a int // 声明,零值 0
a = 10 // 赋值
var b int = 20 // 声明 + 初始化
var c = 30 // 类型推断
d := 40 // 短声明(函数内)
a, b = b, a // 交换,不需要临时变量
fmt.Println(a, b, c, d)Output
20 10 30 40多变量
Go
var x, y int = 1, 2
var p, q = 3, "四"
// 用 _ 丢弃不想要的值
_, err := fmt.Println("返回值个数")
fmt.Println(err)
// 函数多返回值
func minMax(a []int) (int, int) { ... }
lo, hi := minMax([]int{3, 1, 4})作用域与遮蔽
Go
x := 1
fmt.Println("外层", x)
{
x := 2 // 新的 x,遮蔽外层
fmt.Println("内层", x)
}
fmt.Println("外层还是", x)
if x := 99; x > 10 {
fmt.Println("if 里", x) // 只在 if 块内有效
}
// fmt.Println(x) // 这里仍是 1Output
外层 1
内层 2
外层还是 1
if 里 99The dangerous case is
err: := inside an if creates a new err, leaving the outer one nil and silently swallowing the error. go vet's shadow check catches it.值语义
Go
type Point struct{ X, Y int }
p1 := Point{1, 2}
p2 := p1 // 复制一份
p2.X = 100
fmt.Println(p1, p2)
// 想共享就传指针
p3 := &p1
p3.X = 200
fmt.Println(p1)Output
{1 2} {100 2}
{200 2}Slices, maps and channels are value types but hold pointers internally, so after assignment the underlying data is shared. This trips up most Go beginners.
Go
a := []int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a, b) // 都变了Output
[99 2 3] [99 2 3]声明与赋值
Go
var a int // 声明,零值 0
a = 10 // 赋值
var b int = 20 // 声明 + 初始化
var c = 30 // 类型推断
d := 40 // 短声明(函数内)
a, b = b, a // 交换,不需要临时变量
fmt.Println(a, b, c, d)Output
20 10 30 40多变量
Go
var x, y int = 1, 2
var p, q = 3, "四"
// 用 _ 丢弃不想要的值
_, err := fmt.Println("返回值个数")
fmt.Println(err)
// 函数多返回值
func minMax(a []int) (int, int) { ... }
lo, hi := minMax([]int{3, 1, 4})作用域与遮蔽
Go
x := 1
fmt.Println("外层", x)
{
x := 2 // 新的 x,遮蔽外层
fmt.Println("内层", x)
}
fmt.Println("外层还是", x)
if x := 99; x > 10 {
fmt.Println("if 里", x) // 只在 if 块内有效
}
// fmt.Println(x) // 这里仍是 1Output
外层 1
内层 2
外层还是 1
if 里 99遮蔽最危险的场景是
err:在 if 里用 := 新建了一个 err,外层的 err 依然是 nil,错误被静默吞掉。go vet 的 shadow 检查能发现这类问题。值语义
Go
type Point struct{ X, Y int }
p1 := Point{1, 2}
p2 := p1 // 复制一份
p2.X = 100
fmt.Println(p1, p2)
// 想共享就传指针
p3 := &p1
p3.X = 200
fmt.Println(p1)Output
{1 2} {100 2}
{200 2}切片、Map、channel 虽然是值类型,但内部持有指针,赋值后底层数据是共享的。这是 Go 新手最容易误解的地方。
Go
a := []int{1, 2, 3}
b := a
b[0] = 99
fmt.Println(a, b) // 都变了Output
[99 2 3] [99 2 3]