go语言初学者常见错误

本文详细列举了Go语言初学者在编程过程中可能遇到的各种错误,包括大括号放置、变量声明、未使用导入、Map操作、字符串处理、并发安全等方面的问题,并提供了相应的解决方案和示例。通过学习这些常见错误,可以帮助新Go开发者避免不必要的调试时间,更好地理解和掌握Go语言的规范和最佳实践。
摘要由CSDN通过智能技术生成

目录 [−]

  1. 初级
    1. 开大括号不能放在单独的一行
    2. 未使用的变量
    3. 未使用的Imports
    4. 简式的变量声明仅可以在函数内部使用
    5. 使用简式声明重复声明变量
    6. 偶然的变量隐藏Accidental Variable Shadowing
    7. 不使用显式类型,无法使用“nil”来初始化变量
    8. 使用“nil” Slices and Maps
    9. Map的容量
    10. 字符串不会为nil
    11. Array函数的参数
    12. 在Slice和Array使用“range”语句时的出现的不希望得到的值
    13. Slices和Arrays是一维的
    14. 访问不存在的Map Keys
    15. Strings无法修改
    16. String和Byte Slice之间的转换
    17. String和索引操作
    18. 字符串不总是UTF8文本
    19. 字符串的长度
    20. 在多行的Slice、Array和Map语句中遗漏逗号
    21. log.Fatal和log.Panic不仅仅是Log
    22. 内建的数据结构操作不是同步的
    23. String在“range”语句中的迭代值
    24. 对Map使用“for range”语句迭代
    25. "switch"声明中的失效行为
    26. 自增和自减
    27. 按位NOT操作
    28. 操作优先级的差异
    29. 未导出的结构体不会被编码
    30. 有活动的Goroutines下的应用退出
    31. 向无缓存的Channel发送消息,只要目标接收者准备好就会立即返回
    32. 向已关闭的Channel发送会引起Panic
    33. 使用"nil" Channels
    34. 传值方法的接收者无法修改原有的值
  2. 中级
    1. 关闭HTTP的响应
    2. 关闭HTTP的连接
    3. 比较Structs, Arrays, Slices, and Maps
    4. 从Panic中恢复
    5. 在Slice, Array, and Map "range"语句中更新引用元素的值
    6. 在Slice中"隐藏"数据
    7. Slice的数据“毁坏”
    8. 陈旧的(Stale)Slices
    9. 类型声明和方法
    10. 从"for switch"和"for select"代码块中跳出
    11. "for"声明中的迭代变量和闭包
    12. Defer函数调用参数的求值
    13. 被Defer的函数调用执行
    14. 失败的类型断言
    15. 阻塞的Goroutine和资源泄露
  3. 高级
    1. 使用指针接收方法的值的实例
    2. 更新Map的值
    3. "nil" Interfaces和"nil" Interfaces的值
    4. 栈和堆变量
    5. GOMAXPROCS, 并发, 和并行
    6. 读写操作的重排顺序
    7. 优先调度

原文: 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

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尹成

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值