1.左大括号 { 不能单独放一行
在其他大多数语言中,{ 的位置你自行决定。Go比较特别,遵守分号注入规则(automatic semicolon injection):编译器会在每行代码尾部特定分隔符后加;来分隔多条语句,比如会在 ) 后加分号:
// 错误示例
func main()
{
println("www.topgoer.com是个不错的go语言中文文档")
}
// 等效于
func main(); // 无函数体
{
println("hello world")
}
./main.go: missing function body
./main.go: syntax error: unexpected semicolon or newline before {
// 正确示例
func main() {
println("www.topgoer.com是个不错的go语言中文文档")
}
2.未使用的变量
如果在函数体代码中有未使用的变量,则无法通过编译,不过全局变量声明但不使用是可以的。即使变量声明后为变量赋值,依旧无法通过编译,需在某处使用它:
// 错误示例
var gvar int // 全局变量,声明不使用也可以
func main() {
var one int // error: one declared and not used
two := 2 // error: two declared and not used
var three int // error: three declared and not used
three = 3
}
// 正确示例
// 可以直接注释或移除未使用的变量
func main() {
var one int
_ = one
two := 2
println(two)
var three int
one = three
var four int
four = four
}
3.未使用的 import
如果你 import一个包,但包中的变量、函数、接口和结构体一个都没有用到的话,将编译失败。可以使用 _下划线符号作为别名来忽略导入的包,从而避免编译错误,这只会执行 package 的 init()
// 错误示例
import (
"fmt" // imported and not used: "fmt"
"log" // imported and not used: "log"
"time" // imported and not used: "time"
)
func main() {
}
// 正确示例
// 可以使用 goimports 工具来注释或移除未使用到的包
import (
_ "fmt"
"log"
"time"
)
func main() {
_ = log.Println
_ = time.Now
}
4.简短声明的变量只能在函数内部使用
// 错误示例
myvar := 1 // syntax error: non-declaration statement outside function body
func main() {
}
// 正确示例
var myvar = 1
func main() {
}
5.使用简短声明来重复声明变量
不能用简短声明方式来单独为一个变量重复声明,:=左侧至少有一个新变量,才允许多变量的重复声明:
// 错误示例
func main() {
one := 0
one := 1 // error: no new variables on left side of :=
}
// 正确示例
func main() {
one := 0
one, two := 1, 2 // two 是新变量,允许 one 的重复声明。比如 error 处理经常用同名变量 err
one, two = two, one // 交换两个变量值的简写
}
6.不能使用简短声明来设置字段的值
struct 的变量字段不能使用 := 来赋值以使用预定义的变量来避免解决:
// 错误示例
type info struct {
result int
}
func work() (int, error) {
return 3, nil
}
func main() {
var data info
data.result, err := work() // error: non-name data.result on left side of :=
fmt.Printf("info: %+v\n", data)
}
// 正确示例
func main() {
var data info
var err error // err 需要预声明
data.result, err = work()
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("info: %+v\n", data)
}
7.不小心覆盖了变量
对从动态语言转过来的开发者来说,简短声明很好用,这可能会让人误会 := 是一个赋值操作符。如果你在新的代码块中像下边这样误用了 :=,编译不会报错,但是变量不会按你的预期工作:
func main() {
x := 1
println(x) // 1
{
println(x) // 1
x := 2
println(x) // 2 // 新的 x 变量的作用域只在代码块内部
}
println(x) // 1
}
这是 Go 开发者常犯的错,而且不易被发现。可使用 vet工具来诊断这种变量覆盖,Go 默认不做覆盖检查,添加 -shadow 选项来启用:
> go tool vet -shadow main.go
main.go:9: declaration of "x" shadows declaration at main.go:5
注意 vet 不会报告全部被覆盖的变量,可以使用 go-nyet 来做进一步的检测:
> $GOPATH/bin/go-nyet main.go
main.go:10:3:Shadowing variable `x`
8.显式类型的变量无法使用 nil 来初始化
nil 是 interface、function、pointer、map、slice 和 channel 类型变量的默认初始值。但声明时不指定类型,编译器也无法推断出变量的具体类型。
// 错误示例
func main() {
var x = nil // error: use of untyped nil
_ = x
}
// 正确示例
func main() {
var x interface{} = nil
_ = x
}
9.直接使用值为 nil 的 slice、map
允许对值为 nil 的 slice 添加元素,但对值为 nil 的 map添加元素则会造成运行时 panic
// map 错误示例
func main() {
var m map[string]int
m["one"] = 1 // error: panic: assignment to entry in nil map
// m := make(map[string]int)// map 的正确声明,分配了实际的内存
}
// slice 正确示例
func main() {
var s []int
s = append(s, 1)
}
10.map 容量
在创建 map 类型的变量时可以指定容量,但不能像 slice 一样使用 cap() 来检测分配空间的大小:
// 错误示例
func main() {
m := make(map[string]int, 99)
println(cap(m)) // error: invalid argument m1 (type map[string]int) for cap
}