Golang中短赋值语句为何看起来奇怪
Golang中短赋值语句为何看起来奇怪 在学习Golang的过程中,我接触到了短变量声明语句,但当它在源文件中被使用两次或更多次时,确实令人困惑。
// 更多代码
err := value
....
...
err : value
// 更多代码
运行这类代码后,我遇到了这种错误 -> /prog.go:10:4: no new variables on left side of :=
除此之外,我编写了一些通用的Golang代码,但对于上面提到的相同情况,它并没有抛出任何错误。
代码链接:https://play.golang.org/p/JKa0Q7vVwC4
package main
import (
"fmt"
"log"
)
func main() {
value1, err := "VALUE1", "ERROR"
if err != "ERROR" {
log.Fatal(err)
}
fmt.Println(value1)
value2, err := "VALUE2", "Error"
if err != "Error" {
log.Fatal(err)
}
fmt.Println(value2)
}
输出
VALUE1
VALUE2
Program exited.
在这段代码中,err 变量使用了2次 := 短变量声明语句。它确实在没有任何异常的情况下运行。
社区的朋友们,请向我解释一下这是如何发生的。
更多关于Golang中短赋值语句为何看起来奇怪的实战教程也可以访问 https://www.itying.com/category-94-b0.html
感谢 @hollowaykeanho ❤️
更多关于Golang中短赋值语句为何看起来奇怪的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
谢谢你 @petrus ❤️
你好 @hollowaykeanho,感谢你提供的有价值的更新,但我仍然有些困惑。
value1, err := "VALUE1", "ERROR"
....
value2, err := "VALUE2", "ERROR"
在源代码中,这两种写法都不会引发异常。它定义并声明了同一个变量两次。
https://play.golang.org/p/JKa0Q7vVwC4
还有更多
https://play.golang.org/p/xDD57i4Ohrs 这两个变量甚至拥有相同的地址。
ajilraju:
这两种情况在源代码中都不会导致异常。
它从来不需要,因为:
- 这不是对引用变量的赋值。
value2确实需要声明。如果我将其分解:
value1, err := "v1", "e1" # value 1 = [声明并定义], err = [声明并定义]
...
value2, err := "v2", "e2" # value 2 = [声明并定义], err = [定义]
...
value3, err := "v3", "e3" # value 3 = [声明并定义], err = [定义]
...
value2, err = "v4", "e4" # value 2 = [定义], err = [定义]
...
value2, err := "v5", "e5" # 错误:value 2 = [重新声明并定义], err = [重新声明并定义]
当涉及多个变量时,Go 接受这种简短赋值。你的第二个例子与我的解释一致(err 应该是同一个对象)。
value1, err := "VALUE1", "ERROR"
value2, err := "VALUE2", "Error"
你的第二个短赋值之所以能通过,是因为 value2 对于“声明并定义”来说确实是新的变量。Go 允许在存在新变量的情况下进行多次短赋值。
然而,上述规则不适用于引用中的赋值(例如结构体字段)。在这种情况下,你需要先声明 err 变量,然后再进行相应的赋值。例如,以下代码无法工作:
s := &MyStruct{A: 0} // 某种结构体
s.A, err := somefunc(...) // err 是新变量
以下代码可以工作:
var err error
s := &MyStruct{A: 0} // 某种结构体
s.A, err = somefunc(...)
解释
短赋值仅仅是“声明并定义”的一种快捷方式,例如:
var err error // 声明 err
err = someFunc(...) // 定义 err
err = anotherFunc(...) // 定义 err
可以合并为一行:
err := someFunc(...) // 声明并定义 err
err = anotherFunc(...) // 定义 err
一般经验法则是,每个变量只能 声明 一次,并且在声明之后可以 定义 多次。因此,以下代码无法工作:
err := someFunc(...) # 声明并定义
err := anotherFunc(...) # 错误:重复声明并定义
请阅读Go编程语言规范。
特别是短变量声明。
与常规变量声明不同,短变量声明可以重新声明变量,前提是这些变量最初是在同一代码块(或者如果该块是函数体,则在参数列表中)以相同类型声明的,并且至少有一个非空白变量是新的。因此,重新声明只能出现在多变量的短声明中。重新声明不会引入新变量;它只是为原始变量分配一个新值。
运行以下程序:
package main
import (
"fmt"
"log"
)
func main() {
value1, err := "VALUE1", "ERROR"
fmt.Printf("%p %p\n", &value1, &err)
if err != "ERROR" {
log.Fatal(err)
}
fmt.Println(value1, err)
value2, err := "VALUE2", "Error"
fmt.Printf("%p %p\n", &value2, &err)
if err != "Error" {
log.Fatal(err)
}
fmt.Println(value2, err)
}

Go Playground - The Go Programming Language
0xc0000961e0 0xc0000961f0
VALUE1 ERROR
0xc000096220 0xc0000961f0
VALUE2 Error
输出结果符合预期。
短赋值语句(:=)在Go中的行为取决于左侧变量的声明状态。在你的第一个例子中,错误是因为在同一个作用域内重复声明同一个变量,而第二个例子能正常运行是因为它满足短变量声明的特殊规则。
关键规则:短变量声明(:=)要求左侧至少有一个变量是未声明的。当左侧变量都已存在时,:= 会退化为普通赋值。
示例分析
错误示例的原因
err := value1 // 第一次声明err
// ...
err := value2 // 错误:左侧没有新变量
这里第二次使用 := 时,err 已经存在,且左侧没有其他新变量,所以编译失败。
你的可运行代码分析
package main
import (
"fmt"
"log"
)
func main() {
// 第一次声明:value1和err都是新变量
value1, err := "VALUE1", "ERROR"
if err != "ERROR" {
log.Fatal(err)
}
fmt.Println(value1)
// 第二次声明:value2是新变量,err已存在
// 满足"至少有一个新变量"的规则,所以合法
// err被重新赋值,value2被声明
value2, err := "VALUE2", "Error"
if err != "Error" {
log.Fatal(err)
}
fmt.Println(value2)
}
更多示例
示例1:混合声明和赋值
package main
func main() {
var x int
y := 10 // y是新变量
x, y := 5, 6 // x已存在,y已存在,但:=合法吗?
// 错误:no new variables on left side of :=
}
示例2:至少一个新变量
package main
func main() {
a := 1
b := 2
// 合法:c是新变量
a, b, c := 3, 4, 5
println(a, b, c) // 输出: 3 4 5
// 合法:d是新变量
b, c, d := 6, 7, 8
println(b, c, d) // 输出: 6 7 8
}
示例3:作用域的影响
package main
func main() {
x := 1
if true {
// 这是新的作用域,x是新变量(遮蔽外层的x)
x, y := 2, 3
println(x, y) // 输出: 2 3
}
println(x) // 输出: 1(外层x未改变)
}
示例4:函数返回值的处理
package main
func getValues() (int, error) {
return 42, nil
}
func main() {
// 第一次声明
value, err := getValues()
// 第二次:只有value是新变量
value, err := getValues() // 错误:没有新变量
// 正确写法:至少有一个新变量
newValue, err := getValues() // 合法:newValue是新变量
_ = newValue
}
实际应用场景
package main
import (
"io"
"os"
)
func processFile() error {
// 第一次打开文件
file1, err := os.Open("file1.txt")
if err != nil {
return err
}
defer file1.Close()
// 处理file1...
// 第二次打开文件 - 合法,因为file2是新变量
file2, err := os.Open("file2.txt")
if err != nil {
return err
}
defer file2.Close()
// 处理file2...
return nil
}
func copyFile(src, dst string) error {
// 打开源文件
srcFile, err := os.Open(src)
if err != nil {
return err
}
defer srcFile.Close()
// 创建目标文件 - 合法,dstFile是新变量
dstFile, err := os.Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
// 复制文件 - 这里err被重新赋值
_, err = io.Copy(dstFile, srcFile)
return err
}
总结:短变量声明(:=)在左侧至少有一个新变量时是合法的。在你的可运行示例中,第二次使用 value2, err := ... 时,value2 是新变量,所以编译通过,err 被重新赋值。这是一个有用的特性,允许在需要声明新变量的同时重用错误变量。

