目录 [−]
- 初级
- 开大括号不能放在单独的一行
- 未使用的变量
- 未使用的Imports
- 简式的变量声明仅可以在函数内部使用
- 使用简式声明重复声明变量
- 偶然的变量隐藏Accidental Variable Shadowing
- 不使用显式类型,无法使用“nil”来初始化变量
- 使用“nil” Slices and Maps
- Map的容量
- 字符串不会为nil
- Array函数的参数
- 在Slice和Array使用“range”语句时的出现的不希望得到的值
- Slices和Arrays是一维的
- 访问不存在的Map Keys
- Strings无法修改
- String和Byte Slice之间的转换
- String和索引操作
- 字符串不总是UTF8文本
- 字符串的长度
- 在多行的Slice、Array和Map语句中遗漏逗号
- log.Fatal和log.Panic不仅仅是Log
- 内建的数据结构操作不是同步的
- String在“range”语句中的迭代值
- 对Map使用“for range”语句迭代
- "switch"声明中的失效行为
- 自增和自减
- 按位NOT操作
- 操作优先级的差异
- 未导出的结构体不会被编码
- 有活动的Goroutines下的应用退出
- 向无缓存的Channel发送消息,只要目标接收者准备好就会立即返回
- 向已关闭的Channel发送会引起Panic
- 使用"nil" Channels
- 传值方法的接收者无法修改原有的值
- 中级
- 高级
原文: 50 Shades of Go: Traps, Gotchas, and Common Mistakes for New Golang Devs
翻译: Go的50度灰:新Golang开发者要注意的陷阱、技巧和常见错误, 译者: 影风LEY
Go是一门简单有趣的语言,但与其他语言类似,它会有一些技巧。。。这些技巧的绝大部分并不是Go的缺陷造成的。如果你以前使用的是其他语言,那么这其中的有些错误就是很自然的陷阱。其它的是由错误的假设和缺少细节造成的。
如果你花时间学习这门语言,阅读官方说明、wiki、邮件列表讨论、大量的优秀博文和Rob Pike的展示,以及源代码,这些技巧中的绝大多数都是显而易见的。尽管不是每个人都是以这种方式开始学习的,但也没关系。如果你是Go语言新人,那么这里的信息将会节约你大量的调试代码的时间。
初级
开大括号不能放在单独的一行
在大多数其他使用大括号的语言中,你需要选择放置它们的位置。Go的方式不同。你可以为此感谢下自动分号的注入(没有预读)。是的,Go中也是有分号的:-)
失败的例子:
1 2 3 4 5 6 7 8 |
package main import "fmt" func main() { //error, can't have the opening brace on a separate line fmt.Println("hello there!") } |
编译错误:
/tmp/sandbox826898458/main.go:6: syntax error: unexpected semicolon or newline before {
有效的例子:
1 2 3 4 5 6 7 |
package main import "fmt" func main() { fmt.Println("works!") } |
未使用的变量
如果你有未使用的变量,代码将编译失败。当然也有例外。在函数内一定要使用声明的变量,但未使用的全局变量是没问题的。
如果你给未使用的变量分配了一个新的值,代码还是会编译失败。你需要在某个地方使用这个变量,才能让编译器愉快的编译。
Fails:
1 2 3 4 5 6 7 8 9 10 |
package main var gvar int //not an error func main() { var one int //error, unused variable two := 2 //error, unused variable var three int //error, even though it's assigned 3 on the next line three = 3 } |
Compile Errors:
/tmp/sandbox473116179/main.go:6: one declared and not used
/tmp/sandbox473116179/main.go:7: two declared and not used
/tmp/sandbox473116179/main.go:8: three declared and not used
Works:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package main import "fmt" func main() { var one int _ = one two := 2 fmt.Println(two) var three int three = 3 one = three var four int four = four } |
另一个选择是注释掉或者移除未使用的变量 :-)
未使用的Imports
如果你引入一个包,而没有使用其中的任何函数、接口、结构体或者变量的话,代码将会编译失败。
你可以使用goimports来增加引入或者移除未使用的引用:
1 |
$ go get golang.org/x/tools/cmd/goimports |
如果你真的需要引入的包,你可以添加一个下划线标记符,_,来作为这个包的名字,从而避免编译失败。下滑线标记符用于引入,但不使用。
Fails:
1 2 3 4 5 6 7 8 9 10 |
package main import ( "fmt" "log" "time" ) func main() { } |
Compile Errors:
/tmp/sandbox627475386/main.go:4: imported and not used: "fmt"
/tmp/sandbox627475386/main.go:5: imported and not used: "log"
/tmp/sandbox627475386/main.go:6: imported and not used: "time"
Works:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
package main import ( _ "fmt" "log" "time" ) var _ = log.Println func main() { _ = time.Now } |
另一个选择是移除或者注释掉未使用的imports :-)
简式的变量声明仅可以在函数内部使用
Fails:
1 2 3 4 5 6 |
package main myvar := 1 //error func main() { } |
Compile Error:
/tmp/sandbox265716165/main.go:3: non-declaration statement outside function body
Works:
1 2 3 4 5 6 |
package main var myvar = 1 func main() { } |
使用简式声明重复声明变量
你不能在一个单独的声明中重复声明一个变量,但在多变量声明中这是允许的,其中至少要有一个新的声明变量。
重复变量需要在相同的代码块内,否则你将得到一个隐藏变量。
Fails:
1 2 3 4 5 6 |
package main func main() { one := 0 one := 1 //error } |
Compile Error:
/tmp/sandbox706333626/main.go:5: no new variables on left side of :=
Works:
1 2 3 4 5 6 7 8 |
package main func main() { one := 0 one, two := 1,2 one,two = two,one } |
偶然的变量隐藏Accidental Variable Shadowing
短式变量声明的语法如此的方便(尤其对于那些使用过动态语言的开发者而言),很容易让人把它当成一个正常的分配操作。如果你在一个新的代码块中犯了这个错误,将不会出现编译错误,但你的应用将不会做你所期望的事情。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
package main import "fmt" func main() { x := 1 fmt.Println(x) //prints 1 { fmt.Println(x) //prints 1 x := 2 fmt.Println(x) //prints 2 } fmt.Println(x) //prints 1 (bad if you need 2) } |
即使对于经验丰富的Go开发者而言,这也是一个非常常见的陷阱。这个坑很容易挖,但又很难发现。
你可以使用 vet命令来发现一些这样的问题。 默认情况下, vet
不会执行这样的检查,你需要设置-shadow
参数:go tool vet -shadow your_file.go
。
不使用显式类型,无法使用“nil”来初始化变量
nil
标志符用于表示interface、函数、maps、slices和channels的“零值”。如果你不指定变量的类型,编译器将无法编译你的代码,因为它猜不出具体的类型。
Fails:
1 2 3 4 5 6 7 |
package main func main() { var x = nil //error _ = x } |
Compile Error:
/tmp/sandbox188239583/main.go:4: use of untyped nil
Works:
1 2 3 4 5 6 7 |
package main func main() { var x interface{} = nil _ = x } |
使用“nil” Slices and Maps
在一个nil
的slice中添加元素是没问题的,但对一个map做同样的事将会生成一个运行时的panic。
Works:
1 2 3 4 5 6 |
package main func main() { var s []int s = append(s,1) } |
Fails:
1 2 3 4 5 6 7 |
package main func main() { var m map[string]int m["one"] = 1 //error } |
Map的容量
你可以在map创建时指定它的容量,但你无法在map上使用cap()函数。
Fails:
1 2 3 4 5 6 |
package main func main() { m := make(map[string]int,99) cap(m) //error } |
Compile Error:
/tmp/sandbox326543983/main.go:5: invalid argument m (type map[string]int) for cap
字符串不会为nil
这对于经常使用nil
分配字符串变量的开发者而言是个需要注意的地方。
Fails:
1 2 3 4 5 6 7 8 9 |
package main func main() { var x string = nil //erro |