when you're randomizingvue componentt values such as x,y,and z什么意思

点击阅读原文
Go 语言从新手到大神:每个人都会踩的五十个坑
5月18日 发布,来源:
Go语言是一个简单却蕴含深意的语言。但是,即便号称是最简单的C语言,都能总结出一本《C陷阱与缺陷》,更何况Go语言呢。Go语言中的许多坑其实并不是因为Go自身的问题。一些错误你再别的语言中也会犯,例如作用域,一些错误就是对因为 Go 语言的特性不了解而导致的,例如 range。
其实如果你在学习Go语言的时候去认真地阅读官方文档,百科,邮件列表或者其他的类似 Rob Pike 的名人博客,报告,那么本文中提到的许多坑都可以避免。但是不是每个人都会从基础学起,例如译者就喜欢简单粗暴地直接用Go语言写程序。如果你也像译者一样,那么你应该读一下这篇文章:这样可以避免在调试程序时浪费过多时间。
本文将50个坑按照使用使用范围和难易程度分为以下三个级别:“新手入门级”,“新手深入级”,“新手进阶级”。
/tmp/sandbox/main.go:6: syntax error: unexpected semicolon or newline before {
package main
import "fmt"
func main() {
fmt.Println("works!")
未使用已定义的变量
级别:新手入门级
如果代码中有未使用的变量,那个代码编译的时候就会报错。Go要求在代码中所有声明的变量都需要被用到,当然,全局变量除外。
函数的参数也可以只被声明,不被使用。
对于未声明变量的调用同样会导致编译失败。和C语言一样,Go编译器也是个女人,他说什么你都要尽力满足。
出错代码:
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
func(unused string) {
fmt.Println("Unused arg. No compile error")
}("what?")
错误信息:
/tmp/sandbox/main.go:6: one declared and not used /tmp/sandbox/main.go:7: two declared and not used /tmp/sandbox/main.go:8: three declared and not used
修正代码:
package main
import "fmt"
func main() {
var one int
fmt.Println(two)
var three int
one = three
var four int
four = four
当然,你也可以考虑删除那些没有使用的变量。
未使用的包
级别:新手入门级
当import一个包之后,如果不使用这个包,或者这个包中的函数/接口/数据结构/变量,那么将会编译失败。
如果真的确认要引入变量但是不使用的话,我们可以用“”标识符坐标记,避免编译失败。“”标识符表示为了得到这些包的副作用而引入这些包。
出错代码:
package main
func main() {
错误信息:
/tmp/sandbox/main.go:4: imported and not used: "fmt"
/tmp/sandbox/main.go:5: imported and not used: "log"
/tmp/sandbox/main.go:6: imported and not used: "time"
package main
var _ = log.Println
func main() {
_ = time.Now
只能在函数内部使用简短的变量声明
级别:新手入门级
出错代码:
package main
myvar := 1 //error
func main() {
错误信息:
/tmp/sandbox/main.go:3: non-declaration statement outside function body
修正代码:
package main
var myvar = 1
func main() {
无法使用精简的赋值语句对变量重新赋值
级别:新手入门级
不能使用精简的赋值语句重新赋值单个变量,但是可以使用精简的赋值语句同时赋值多个变量。
并且,重定义的变量必须写在同一个代码块。
错误信息:
package main
func main() {
one := 1 //error
错误信息:
/tmp/sandbox/main.go:5: no new variables on left side of :=
修正代码:
package main
func main() {
one, two := 1,2
one,two = two,one
隐式变量(作用域)
级别:新手入门级
和 C 语言一样,Go 语言也有作用于,一个变量的作用范围仅仅是一个代码块。虽然精简的赋值语句很简单,但是注意作用域。
package main
import "fmt"
func main() {
fmt.Println(x) //打印 1
fmt.Println(x) //打印 1
fmt.Println(x) //打印 2
fmt.Println(x) //打印 1 ( 不是 2)
甚至对于有经验的开发者来说,这也是个不注意就会掉进去的深坑。
除非特别指定,否则无法使用 nil 对变量赋值
级别:新手入门级
nil 可以用作 interface、function、pointer、map、slice 和 channel 的“空值”。但是如果不特别指定的话,Go 语言不能识别类型,所以会报错。
错误信息:
package main
func main() {
var x = nil //error
错误信息:
/tmp/sandbox/main.go:4: use of untyped nil
修正代码:
package main
func main() {
var x interface{} = nil
Slice 和 Map 的 nil 值
级别:新手入门级
初始值为 nil 的 Slice 是可以进行“添加”操作的,但是对于 Map 的“添加”操作会导致运行时恐慌。( ﹁ ﹁ ) 恐慌。
修正代码:
package main
func main() {
var s []int
s = append(s,1)
错误信息:
package main
func main() {
var m map[string]int
m["one"] = 1 //error
级别:新手入门级
创建 Map 的时候可以指定 Map 的长度,但是在运行时是无法使用 cap() 功能重新指定 Map 的大小,Map 是定长的。
错误信息:
package main
func main() {
m := make(map[string]int,99)
cap(m) //error
错误信息:
/tmp/sandbox/main.go:5: invalid argument m (type map[string]int) for cap
字符串无法为 nil
级别:新手入门级
所有的开发者都可能踩的坑,在 C 语言中是可以 char *String=NULL,但是 Go 语言中就无法赋值为 nil。
错误信息:
package main
func main() {
var x string = nil //error
if x == nil { //error
x = "default"
Compile Errors:
/tmp/sandbox/main.go:4: cannot use nil as type string in assignment /tmp/sandbox/main.go:6: invalid operation: x == nil (mismatched types string and nil)
修正代码:
package main
func main() {
var x string //defaults to "" (zero value)
if x == "" {
x = "default"
参数中的数组
Array Function Arguments
级别:新手入门级
对于 C 和 C++ 开发者来说,数组就是指针。给函数传递数组就是传递内存地址,对数组的修改就是对原地址数据的修改。但是 Go 语言中,传递的数组不是内存地址,而是原数组的拷贝,所以是无法通过传递数组的方法去修改原地址的数据的。
package main
import "fmt"
func main() {
x := [3]int
func(arr [3]int) {
arr[0] = 7
fmt.Println(arr) //prints [7 2 3]
fmt.Println(x) //prints [1 2 3] (not ok if you need [7 2 3])
如果需要修改原数组的数据,需要使用数组指针(array pointer)。
package main
import "fmt"
func main() {
x := [3]int
func(arr *[3]int) {
(*arr)[0] = 7
fmt.Println(arr) //prints &[7 2 3]
fmt.Println(x) //prints [7 2 3]
或者可以使用 Slice,
package main
import "fmt"
func main() {
x := []int
func(arr []int) {
arr[0] = 7
fmt.Println(arr) //prints [7 2 3]
fmt.Println(x) //prints [7 2 3]
使用 Slice 和 Array 的 range 会导致预料外的结果
级别:新手入门级
如果你对别的语言中的 for in 和 foreach 熟悉的话,那么 Go 中的 range 使用方法完全不一样。因为每次的 range 都会返回两个值,第一个值是在 Slice 和 Array 中的编号,第二个是对应的数据。
出错代码:
package main
import "fmt"
func main() {
x := []string{"a","b","c"}
for v := range x {
fmt.Println(v) //prints 0, 1, 2
修正代码:
package main
import "fmt"
func main() {
x := []string{"a","b","c"}
for _, v := range x {
fmt.Println(v) //prints a, b, c
Slive 和 Array 维度是一维
级别:新手入门级
Go 看上去支持多维的 Array 和 Slice,但是其实不然。尽管可以创建 Array 的 Array,也可以创建 Slice 的 Slice。对于依赖多维 Array 的计算密集型的程序,无论是从性能还是复杂程度,Go 都不是最佳选择。
当然,如果你选择创建嵌套的 Array 与嵌套的 Slice,那么你就得自己负责进行索引、进行下表检查、以及 Array 增长时的内存分配。嵌套 Slice 分为两种,Slice 中嵌套独立的 Slice,或者 Slice 中嵌套共享数据的 Slice。
使用嵌套的独立 Slice 创建多维的 Array 需要两步。第一步,创建外围 Slice,然后分配每个内部的 Slice。内部的 Slice 是独立的,可以对每个单独的内部 Slice 进行缩放。
package main
func main() {
table := make([][]int,x)
for i:= range table {
table[i] = make([]int,y)
使用嵌套、共享数据的 Slive 创建多维 Array 需要三步。第一,创建数据“容器”,第二部,创建外围 Slice,第三部,对内部的 Slice 进行初始化。
package main
import "fmt"
func main() {
h, w := 2, 4
raw := make([]int,h*w)
for i := range raw {
raw[i] = i
fmt.Println(raw,&raw[4])
//prints: [0 1 2 3 4 5 6 7] &ptr_addr_x&
table := make([][]int,h)
for i:= range table {
table[i] = raw[i*w:i*w + w]
fmt.Println(table,&table[1][0])
//prints: [[0 1 2 3] [4 5 6 7]] &ptr_addr_x&
Go 语言也有对于支持多维 Array 和 Slice 的提案,不过不要期待太多。Go 语言官方将这些需求分在“低优先级”组中。
试图访问不存在的 Map 键值
级别:新手入门级
并不能在所有情况下都能通过判断 map 的记录值是不是 nil 判断记录是否存在。在 Go 语言中,对于“零值”是 nil 的数据类型可以这样判断,但是其他的数据类型不可以。简而言之,这种做法并不可靠(例如布尔变量的“零值”是 false)。最可靠的做法是检查 map 记录的第二返回值。
错误代码:
package main
import "fmt"
func main() {
x := map[string]string{"one":"a","two":"","three":"c"}
if v := x["two"]; v == "" { //incorrect
fmt.Println("no entry")
修正代码:
package main
import "fmt"
func main() {
x := map[string]string{"one":"a","two":"","three":"c"}
if _,ok := x["two"]; !ok {
fmt.Println("no entry")
String 不可变
级别:新手入门级
对于 String 中单个字符的操作会导致编译失败。String 是带有一些附加属性的只读的字节片(Byte Slices)。所以如果想要对 String 操作的话,应当使用字节片操作,而不是将它转换为 String 类型。
错误信息:
package main
import "fmt"
func main() {
x := "text"
x[0] = 'T'
fmt.Println(x)
错误信息:
/tmp/sandbox/main.go:7: cannot assign to x[0]
修正代码:
package main
import "fmt"
func main() {
x := "text"
xbytes := []byte(x)
xbytes[0] = 'T'
fmt.Println(string(xbytes)) //prints Text
注意这里的操作并不就是最正确的操作,因为有些字符可能会存储在多个字节中。如果你的开发情景有这种情况的话,需要先把 String 转换为 rune 格式。即便是 rune,一个字符也可能会保存在多个 rune 中,因此 Go String 也表现为字节序列(String Sequences)。
String 与 Byte Slice 的转换
级别:新手入门级
当将 String 类型和 Byte Slice 类型互相转化的时候,得到的新数据都是原数据的拷贝,而不是原数据。类型转化和切片重组(Resliciing)不一样,切片重组后的变量仍然指向原变量,而类型转换后的变量指向原变量的拷贝。
Go 语言已经对 []byte 和 String 类型的互相转化做了优化,并且还会继续优化。
The first optimization avoids extra allocations when []byte keys are used to lookup entries in map[string] collections: m[string(key)].
一个优化是
The second optimization avoids extra allocations in for range clauses where strings are converted to []byte: for i,v := range []byte(str) {...}.
Strings and Index Operator
级别:新手入门级
The index operator on a string returns a byte value, not a character (like it's done in other languages).
package main
import "fmt"
func main() {
x := "text"
fmt.Println(x[0]) //print 116
fmt.Printf("%T",x[0]) //prints uint8
If you need to access specific string "characters" (unicode code points/runes) use the for range clause. The official "unicode/utf8" package and the experimental utf8string package (golang.org/x/exp/utf8string) are also useful. The utf8string package includes a convenient At() method. Converting the string to a slice of runes is an option too.
Strings Are Not Always UTF8 Text
级别:新手入门级
String values are not required to be UTF8 text. They can contain arbitrary bytes. The only time strings are UTF8 is when string literals are used. Even then they can include other data using escape sequences.
To know if you have a UTF8 text string use the ValidString() function from the "unicode/utf8" package.
package main
"unicode/utf8"
func main() {
data1 := "ABC"
fmt.Println(utf8.ValidString(data1)) //prints: true
data2 := "A\xfeC"
fmt.Println(utf8.ValidString(data2)) //prints: false
String Length
级别:新手入门级
Let's say you are a python developer and you have the following piece of code:
data = u'?'
print(len(data)) #prints: 1
When you convert it to a similar Go code snippet you might be surprised.
package main
import "fmt"
func main() {
data := "?"
fmt.Println(len(data)) //prints: 3
The built-in len() function returns the number of bytes instead of the number of characters like it's done for unicode strings in Python.
To get the same results in Go use the RuneCountInString() function from the "unicode/utf8" package.
package main
"unicode/utf8"
func main() {
data := "?"
fmt.Println(utf8.RuneCountInString(data)) //prints: 1
Technically the RuneCountInString() function doesn't return the number of characters because a single character may span multiple runes.
package main
"unicode/utf8"
func main() {
data := "e?"
fmt.Println(len(data)) //prints: 3
fmt.Println(utf8.RuneCountInString(data)) //prints: 2
Missing Comma In Multi-Line Slice, Array, and Map Literals
级别:新手入门级
错误信息:
package main
func main() {
x := []int{
Compile Errors:
/tmp/sandbox/main.go:6: syntax error: need trailing comma before newline in composite literal /tmp/sandbox/main.go:8: non-declaration statement outside function body /tmp/sandbox/main.go:9: syntax error: unexpected }
修正代码:
package main
func main() {
x := []int{
y := []int //no error
You won't get a compiler error if you leave the trailing comma when you collapse the declaration to be on a single line.
log.Fatal and log.Panic Do More Than Log
级别:新手入门级
Logging libraries often provide different log levels. Unlike those logging libraries, the log package in Go does more than log if you call its Fatal() and Panic() functions. When your app calls those functions Go will also terminate your app :-)
package main
import "log"
func main() {
log.Fatalln("Fatal Level: log entry") //app exits here
log.Println("Normal Level: log entry")
Built-in Data Structure Operations Are Not Synchronized
级别:新手入门级
Even though Go has a number of features to support concurrency natively, concurrency safe data collections are not one them :-) It's your responsibility to ensure the data collection updates are atomic. Goroutines and channels are the recommended way to implement those atomic operations, but you can also leverage the "sync" package if it makes sense for your application.
Iteration Values For Strings in "range" Clauses
级别:新手入门级
The index value (the first value returned by the "range" operation) is the index of the first byte for the current "character" (unicode code point/rune) returned in the second value. It's not the index for the current "character" like it's done in other languages. Note that an actual character might be represented by multiple runes. Make sure to check out the "norm" package (golang.org/x/text/unicode/norm) if you need to work with characters.
The for range clauses with string variables will try to interpret the data as UTF8 text. For any byte sequences it doesn't understand it will return 0xfffd runes (aka unicode replacement characters) instead of the actual data. If you have arbitrary (non-UTF8 text) data stored in your string variables, make sure to convert them to byte slices to get all stored data as is.
package main
import "fmt"
func main() {
data := "A\xfe\x02\xff\x04"
for _,v := range data {
fmt.Printf("%#x ",v)
//prints: 0x41 0xfffd 0x2 0xfffd 0x4 (not ok)
fmt.Println()
for _,v := range []byte(data) {
fmt.Printf("%#x ",v)
//prints: 0x41 0xfe 0x2 0xff 0x4 (good)
Iterating Through a Map Using a "for range" Clause
级别:新手入门级
This is a gotcha if you expect the items to be in a certain order (e.g., ordered by the key value). Each map iteration will produce different results. The Go runtime tries to go an extra mile randomizing the iteration order, but it doesn't always succeed so you may get several identical map iterations. Don't be surprised to see 5 identical iterations in a row.
package main
import "fmt"
func main() {
m := map[string]int{"one":1,"two":2,"three":3,"four":4}
for k,v := range m {
fmt.Println(k,v)
And if you use the Go Playground () you'll always get the same results because it doesn't recompile the code unless you make a change.
Fallthrough Behavior in "switch" Statements
级别:新手入门级
The "case" blocks in "switch" statements break by default. This is different from other languages where the default behavior is to fall through to the next "case" block.
package main
import "fmt"
func main() {
isSpace := func(ch byte) bool {
switch(ch) {
case ' ': //error
case '\t':
return true
return false
fmt.Println(isSpace('\t')) //prints true (ok)
fmt.Println(isSpace(' ')) //prints false (not ok)
You can force the "case" blocks to fall through by using the "fallthrough" statement at the end of each "case" block. You can also rewrite your switch statement to use expression lists in the "case" blocks.
package main
import "fmt"
func main() {
isSpace := func(ch byte) bool {
switch(ch) {
case ' ', '\t':
return true
return false
fmt.Println(isSpace('\t')) //prints true (ok)
fmt.Println(isSpace(' ')) //prints true (ok)
Increments and Decrements
级别:新手入门级
Many languages have increment and decrement operators. Unlike other languages, Go doesn't support the prefix version of the operations. You also can't use these two operators in expressions.
错误信息:
package main
import "fmt"
func main() {
data := []int
++i //error
fmt.Println(data[i++]) //error
Compile Errors:
/tmp/sandbox/main.go:8: syntax error: unexpected ++ /tmp/sandbox/main.go:9: syntax error: unexpected ++, expecting :
修正代码:
package main
import "fmt"
func main() {
data := []int
fmt.Println(data[i])
Bitwise NOT Operator
级别:新手入门级
Many languages use ~ as the unary NOT operator (aka bitwise complement), but Go reuses the XOR operator (^) for that.
错误信息:
package main
import "fmt"
func main() {
fmt.Println(~2) //error
错误信息:
/tmp/sandbox/main.go:6: the bitwise complement operator is ^
修正代码:
package main
import "fmt"
func main() {
var d uint8 = 2
fmt.Printf("%08b\n",^d)
Go still uses ^ as the XOR operator, which may be confusing for some people.
If you want you can represent a unary NOT operation (e.g, NOT 0x02) with a binary XOR operation (e.g., 0x02 XOR 0xff). This could explain why ^ is reused to represent unary NOT operations.
Go also has a special 'AND NOT' bitwise operator (&^), which adds to the NOT operator confusion. It looks like a special feature/hack to support A AND (NOT B) without requiring parentheses.
package main
import "fmt"
func main() {
var a uint8 = 0x82
var b uint8 = 0x02
fmt.Printf("%08b [A]\n",a)
fmt.Printf("%08b [B]\n",b)
fmt.Printf("%08b (NOT B)\n",^b)
fmt.Printf("%08b ^ %08b = %08b [B XOR 0xff]\n",b,0xff,b ^ 0xff)
fmt.Printf("%08b ^ %08b = %08b [A XOR B]\n",a,b,a ^ b)
fmt.Printf("%08b & %08b = %08b [A AND B]\n",a,b,a & b)
fmt.Printf("%08b &^%08b = %08b [A 'AND NOT' B]\n",a,b,a &^ b)
fmt.Printf("%08b&(^%08b)= %08b [A AND (NOT B)]\n",a,b,a & (^b))
Operator Precedence Differences
级别:新手入门级
Aside from the "bit clear" operators (&^) Go has a set of standard operators shared by many other languages. The operator precedence is not always the same though.
package main
import "fmt"
func main() {
fmt.Printf("0x2 & 0x2 + 0x4 -& %#x\n",0x2 & 0x2 + 0x4)
//prints: 0x2 & 0x2 + 0x4 -& 0x6
//Go: (0x2 & 0x2) + 0x4
//C++: 0x2 & (0x2 + 0x4) -& 0x2
fmt.Printf("0x2 + 0x2 && 0x1 -& %#x\n",0x2 + 0x2 && 0x1)
//prints: 0x2 + 0x2 && 0x1 -& 0x6
//Go: 0x2 + (0x2 && 0x1)
//C++: (0x2 + 0x2) && 0x1 -& 0x8
fmt.Printf("0xf | 0x2 ^ 0x2 -& %#x\n",0xf | 0x2 ^ 0x2)
//prints: 0xf | 0x2 ^ 0x2 -& 0xd
//Go: (0xf | 0x2) ^ 0x2
//C++: 0xf | (0x2 ^ 0x2) -& 0xf
Unexported Structure Fields Are Not Encoded
级别:新手入门级
The struct fields starting with lowercase letters will not be (json, xml, gob, etc.) encoded, so when you decode the structure you'll end up with zero values in those unexported fields.
package main
"encoding/json"
type MyData struct {
two string
func main() {
in := MyData
fmt.Printf("%#v\n",in) //prints main.MyData
encoded,_ := json.Marshal(in)
fmt.Println(string(encoded)) //prints {"One":1}
var out MyData
json.Unmarshal(encoded,&out)
fmt.Printf("%#v\n",out) //prints main.MyData
App Exits With Active Goroutines
级别:新手入门级
The app will not wait for all your goroutines to complete. This is a common mistake for beginners in general. Everybody starts somewhere, so there's no shame in making rookie mistakes :-)
package main
func main() {
workerCount := 2
for i := 0; i & workerC i++ {
go doit(i)
time.Sleep(1 * time.Second)
fmt.Println("all done!")
func doit(workerId int) {
fmt.Printf("[%v] is running\n",workerId)
time.Sleep(3 * time.Second)
fmt.Printf("[%v] is done\n",workerId)
You'll see:
[0] is running
[1] is running
One of the most common solutions is to use a "WaitGroup" variable. It will allow the main goroutine to wait until all worker goroutines are done. If your app has long running workers with message processing loops you'll also need a way to signal those goroutines that it's time to exit. You can send a "kill" message to each worker. Another option is to close a channel all workers are receiving from. It's a simple way to signal all goroutines at once.
package main
func main() {
var wg sync.WaitGroup
done := make(chan struct{})
workerCount := 2
for i := 0; i & workerC i++ {
go doit(i,done,wg)
close(done)
fmt.Println("all done!")
func doit(workerId int,done &-chan struct{},wg sync.WaitGroup) {
fmt.Printf("[%v] is running\n",workerId)
defer wg.Done()
fmt.Printf("[%v] is done\n",workerId)
If you run this app you'll see:
[0] is running
[0] is done
[1] is running
[1] is done
Looks like the workers are done before the main goroutine exists. Great! However, you'll also see this:
fatal error: all goroutines are asleep - deadlock!
That's not so great :-) What's going on? Why is there a deadlock? The workers exited and they executed wg.Done(). The app should work.
The deadlock happens because each worker gets a copy of the original "WaitGroup" variable. When workers execute wg.Done() it has no effect on the "WaitGroup" variable in the main goroutine.
package main
func main() {
var wg sync.WaitGroup
done := make(chan struct{})
wq := make(chan interface{})
workerCount := 2
for i := 0; i & workerC i++ {
go doit(i,wq,done,&wg)
for i := 0; i & workerC i++ {
close(done)
fmt.Println("all done!")
func doit(workerId int, wq &-chan interface{},done &-chan struct{},wg *sync.WaitGroup) {
fmt.Printf("[%v] is running\n",workerId)
defer wg.Done()
case m := &- wq:
fmt.Printf("[%v] m =& %v\n",workerId,m)
case &- done:
fmt.Printf("[%v] is done\n",workerId)
Now it works as expected :-)
Sending to an Unbuffered Channel Returns As Soon As the Target Receiver Is Ready
级别:新手入门级
The sender will not be blocked until your message is processed by the receiver. Depending on the machine where you are running the code, the receiver goroutine may or may not have enough time to process the message before the sender continues its execution.
package main
import "fmt"
func main() {
ch := make(chan string)
go func() {
for m := range ch {
fmt.Println("processed:",m)
ch &- "cmd.1"
ch &- "cmd.2" //won't be processed
Sending to an Closed Channel Causes a Panic
级别:新手入门级
Receiving from a closed channel is safe. The ok return value in a receive statement will be set to false indicating that no data was received. If you are receiving from a buffered channel you'll get the buffered data first and once it's empty the ok return value will be false.
Sending data to a closed channel causes a panic. It is a documented behavior, but it's not very intuitive for new Go developers who might expect the send behavior to be similar to the receive behavior.
package main
func main() {
ch := make(chan int)
for i := 0; i & 3; i++ {
go func(idx int) {
ch &- (idx + 1) * 2
//get the first result
fmt.Println(&-ch)
close(ch) //not ok (you still have other senders)
//do other work
time.Sleep(2 * time.Second)
Depending on your application the fix will be different. It might be a minor code change or it might require a change in your application design. Either way, you'll need to make sure your application doesn't try to send data to a closed channel.
The buggy example can be fixed by using a special cancellation channel to signal the remaining workers that their results are no longer neeeded.
package main
func main() {
ch := make(chan int)
done := make(chan struct{})
for i := 0; i & 3; i++ {
go func(idx int) {
case ch &- (idx + 1) * 2: fmt.Println(idx,"sent result")
case &- done: fmt.Println(idx,"exiting")
//get first result
fmt.Println("result:",&-ch)
close(done)
//do other work
time.Sleep(3 * time.Second)
Using "nil" Channels
级别:新手入门级
Send and receive operations on a nil channel block forver. It's a well documented behavior, but it can be a surprise for new Go developers.
package main
func main() {
var ch chan int
for i := 0; i & 3; i++ {
go func(idx int) {
ch &- (idx + 1) * 2
//get first result
fmt.Println("result:",&-ch)
//do other work
time.Sleep(2 * time.Second)
If you run the code you'll see a runtime error like this: fatal error: all goroutines are asleep - deadlock!
This behavior can be used as a way to dynamically enable and disable case blocks in a select statement.
package main
import "fmt"
import "time"
func main() {
inch := make(chan int)
outch := make(chan int)
go func() {
var in &- chan int = inch
var out chan &- int
var val int
case out &- val:
case val = &- in:
out = outch
go func() {
for r := range outch {
fmt.Println("result:",r)
time.Sleep(0)
time.Sleep(3 * time.Second)
Methods with Value Receivers Can't Change the Original Value
级别:新手入门级
Method receivers are like regular function arguments. If it's declared to be a value then your function/method gets a copy of your receiver argument. This means making changes to the receiver will not affect the original value unless your receiver is a map or slice variable and you are updating the items in the collection or the fields you are updating in the receiver are pointers.
package main
import "fmt"
type data struct {
key *string
items map[string]bool
func (this *data) pmethod() {
this.num = 7
func (this data) vmethod() {
this.num = 8
*this.key = "v.key"
this.items["vmethod"] = true
func main() {
key := "key.1"
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=1 key=key.1 items=map[]
d.pmethod()
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=7 key=key.1 items=map[]
d.vmethod()
fmt.Printf("num=%v key=%v items=%v\n",d.num,*d.key,d.items)
//prints num=7 key=v.key items=map[vmethod:true]
Closing HTTP Response Body
level: intermediate
When you make requests using the standard http library you get a http response variable. If you don't read the response body you still need to close it. Note that you must do it for empty responses too. It's very easy to forget especially for new Go developers.
Some new Go developers do try to close the response body, but they do it in the wrong place.
package main
"net/http"
"io/ioutil"
func main() {
resp, err := http.Get("")
defer resp.Body.Close()//not ok
if err != nil {
fmt.Println(err)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
fmt.Println(string(body))
This code works for successful requests, but if the http request fails the resp variable might be nil, which will cause a runtime panic.
The most common why to close the response body is by using a defer call after the http response error check.
package main
"net/http"
"io/ioutil"
func main() {
resp, err := http.Get("")
if err != nil {
fmt.Println(err)
defer resp.Body.Close()//ok, most of the time :-)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
fmt.Println(string(body))
Most of the time when your http request fails the resp variable will be nil and the err variable will be non-nil. However, when you get a redirection failure both variables will be non-nil. This means you can still end up with a leak.
You can fix this leak by adding a call to close non-nil response bodies in the http response error handling block. Another option is to use one defer call to close response bodies for all failed and successful requests.
package main
"net/http"
"io/ioutil"
func main() {
resp, err := http.Get("")
if resp != nil {
defer resp.Body.Close()
if err != nil {
fmt.Println(err)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
fmt.Println(string(body))
The orignal implementation for resp.Body.Close() also reads and discards the remaining response body data. This ensured that the http connection could be reused for another request if the keepalive http connection behavior is enabled. The latest http client behavior is different. Now it's your responsibility to read and discard the remaining response data. If you don't do it the http connection might be closed instead of being reused. This little gotcha is supposed to be documented in Go 1.5.
If reusing the http connection is important for your application you might need to add something like this at the end of your response processing logic:
_, err = io.Copy(ioutil.Discard, resp.Body)
It might be necessary if you don't read the entire response body right away, which might happen if you are processing json http API response with code like this:
json.NewDecoder(resp.Body).Decode(&data)
Closing HTTP Connections
level: intermediate
Some HTTP servers keep network connections open for a while (based on the HTTP 1.1 spec and the server "keep-alive" configurations). By default, the standard http library will close the network connections only when the target HTTP server asks for it. This means your app may run out of sockets/file descriptors under certain conditions.
You can ask the http library to close the connection after your request is done by setting the Close field in the request variable to true.
Another option is to add a Connection request header and set it to close. The target HTTP server should respond with a Connection: close header too. When the http library sees this response header it will also close the connection.
package main
"net/http"
"io/ioutil"
func main() {
req, err := http.NewRequest("GET","",nil)
if err != nil {
fmt.Println(err)
req.Close = true
//or do this:
//req.Header.Add("Connection", "close")
resp, err := http.DefaultClient.Do(req)
if resp != nil {
defer resp.Body.Close()
if err != nil {
fmt.Println(err)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
fmt.Println(len(string(body)))
You can also disable http connection reuse globally. You'll need to create a custom http transport configuration for it.
package main
"net/http"
"io/ioutil"
func main() {
tr := &http.Transport
client := &http.Client
resp, err := client.Get("http://golang.org")
if resp != nil {
defer resp.Body.Close()
if err != nil {
fmt.Println(err)
fmt.Println(resp.StatusCode)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
fmt.Println(err)
fmt.Println(len(string(body)))
If you send a lot of requests to the same HTTP server it's ok to keep the network connection open. However, if your app sends one or two requests to many different HTTP servers in a short period of time it's a good idea to close the network connections right after your app receives the responses. Increasing the open file limit might be a good idea too. The correct solution depends on your application though.
Comparing Structs, Arrays, Slices, and Maps
level: intermediate
You can use the equality operator, ==, to compare struct variables if each structure field can be compared with the equality operator.
package main
import "fmt"
type data struct {
fp float32
complex complex64
str string
events &-chan string
handler interface{}
raw [10]byte
func main() {
v1 := data{}
v2 := data{}
fmt.Println("v1 == v2:",v1 == v2) //prints: v1 == v2: true
If any of the struct fields are not comparable then using the equality operator will result in compile time errors. Note that arrays are comparable only if their data items are comparable.
package main
import "fmt"
type data struct {
num int //ok
checks [10]func() bool //not comparable
doit func() bool //not comparable
m map[string] string //not comparable
bytes []byte //not comparable
func main() {
v1 := data{}
v2 := data{}
fmt.Println("v1 == v2:",v1 == v2)
Go does provide a number of helper functions to compare variables that can't be compared using the comparison operators.
The most generic solution is to use the DeepEqual() function in the reflect package.
package main
type data struct {
num int //ok
checks [10]func() bool //not comparable
doit func() bool //not comparable
m map[string] string //not comparable
bytes []byte //not comparable
func main() {
v1 := data{}
v2 := data{}
fmt.Println("v1 == v2:",reflect.DeepEqual(v1,v2)) //prints: v1 == v2: true
m1 := map[string]string{"one": "a","two": "b"}
m2 := map[string]string{"two": "b", "one": "a"}
fmt.Println("m1 == m2:",reflect.DeepEqual(m1, m2)) //prints: m1 == m2: true
s1 := []int
s2 := []int
fmt.Println("s1 == s2:",reflect.DeepEqual(s1, s2)) //prints: s1 == s2: true
Aside from being slow (which may or may not be a deal breaker for your application), DeepEqual() also has its own gotchas.
package main
func main() {
var b1 []byte = nil
b2 := []byte{}
fmt.Println("b1 == b2:",reflect.DeepEqual(b1, b2)) //prints: b1 == b2: false
DeepEqual() doesn't consider an empty slice to be equal to a "nil" slice. This behavior is different from the behavior you get using the bytes.Equal() function. bytes.Equal() considers "nil" and empty slices to be equal.
package main
func main() {
var b1 []byte = nil
b2 := []byte{}
fmt.Println("b1 == b2:",bytes.Equal(b1, b2)) //prints: b1 == b2: true
DeepEqual() isn't always perfect comparing slices.
package main
"encoding/json"
func main() {
var str string = "one"
var in interface{} = "one"
fmt.Println("str == in:",str == in,reflect.DeepEqual(str, in))
//prints: str == in: true true
v1 := []string{"one","two"}
v2 := []interface{}{"one","two"}
fmt.Println("v1 == v2:",reflect.DeepEqual(v1, v2))
//prints: v1 == v2: false (not ok)
data := map[string]interface{}{
"code": 200,
"value": []string{"one","two"},
encoded, _ := json.Marshal(data)
var decoded map[string]interface{}
json.Unmarshal(encoded, &decoded)
fmt.Println("data == decoded:",reflect.DeepEqual(data, decoded))
//prints: data == decoded: false (not ok)
If your byte slices (or strings) contain text data you might be tempted to use ToUpper() or ToLower() from the "bytes" and "strings" packages when you need to compare values in a case insensitive manner (before using ==,bytes.Equal(), pare()). It will work for English text, but it will not work for text in many other languages. strings.EqualFold() and bytes.EqualFold() should be used instead.
If your byte slices contain secrets (e.g., cryptographic hashes, tokens, etc.) that need to be validated against user-provided data, don't use reflect.DeepEqual(), bytes.Equal(), pare() because those functions will make your application vulnerable to timing attacks. To avoid leaking the timing information use the functions from the 'crypto/subtle' package (e.g., subtle.ConstantTimeCompare()).
Recovering From a Panic
level: intermediate
The recover() function can be used to catch/intercept a panic. Calling recover() will do the trick only when it's done in a deferred function.
Incorrect:
package main
import "fmt"
func main() {
recover() //doesn't do anything
panic("not good")
recover() //won't be executed :)
fmt.Println("ok")
修正代码:
package main
import "fmt"
func main() {
defer func() {
fmt.Println("recovered:",recover())
panic("not good")
The call to recover() works only if it's called directly in your deferred function.
错误信息:
package main
import "fmt"
func doRecover() {
fmt.Println("recovered =&",recover()) //prints: recovered =&
func main() {
defer func() {
doRecover() //panic is not recovered
panic("not good")
Updating and Referencing Item Values in Slice, Array, and Map "range" Clauses
level: intermediate
The data values generated in the "range" clause are copies of the actual collection elements. They are not references to the original items. This means that updating the values will not change the original data. It also means that taking the address of the values will not give you pointers to the original data.
package main
import "fmt"
func main() {
data := []int
for _,v := range data {
v *= 10 //original item is not changed
fmt.Println("data:",data) //prints data: [1 2 3]
If you need to update the original collection record value use the index operator to access the data.
package main
import "fmt"
func main() {
data := []int
for i,_ := range data {
data[i] *= 10
fmt.Println("data:",data) //prints data: [10 20 30]
If your collection holds pointer values then the rules are slightly different. You still need to use the index operator if you want the original record to point to another value, but you can update the data stored at the target location using the second value in the "for range" clause.
package main
import "fmt"
func main() {
data := []*struct {,,}
for _,v := range data {
v.num *= 10
fmt.Println(data[0],data[1],data[2]) //prints & & &
"Hidden" Data in Slices
level: intermediate
When you reslice a slice, the new slice will reference the array of the original slice. If you forget about this behavior it can lead to unexpected memory usage if your application allocates large temporary slices creating new slices from them to refer to small sections of the original data.
package main
import "fmt"
func get() []byte {
raw := make([]byte,10000)
fmt.Println(len(raw),cap(raw),&raw[0]) //prints:
return raw[:3]
func main() {
data := get()
fmt.Println(len(data),cap(data),&data[0]) //prints: 3 10000
To avoid this trap make sure to copy the data you need from the temporary slice (instead of reslicing it).
package main
import "fmt"
func get() []byte {
raw := make([]byte,10000)
fmt.Println(len(raw),cap(raw),&raw[0]) //prints:
res := make([]byte,3)
copy(res,raw[:3])
return res
func main() {
data := get()
fmt.Println(len(data),cap(data),&data[0]) //prints: 3 3
Slice Data "Corruption"
level: intermediate
Let's say you need to rewrite a path (stored in a slice). You reslice the path to reference each directory modifying the first folder name and then you combine the names to create a new path.
package main
func main() {
path := []byte("AAAA/BBBBBBBBB")
sepIndex := bytes.IndexByte(path,'/')
dir1 := path[:sepIndex]
dir2 := path[sepIndex+1:]
fmt.Println("dir1 =&",string(dir1)) //prints: dir1 =& AAAA
fmt.Println("dir2 =&",string(dir2)) //prints: dir2 =& BBBBBBBBB
dir1 = append(dir1,"suffix"...)
path = bytes.Join([][]byte,[]byte{'/'})
fmt.Println("dir1 =&",string(dir1)) //prints: dir1 =& AAAAsuffix
fmt.Println("dir2 =&",string(dir2)) //prints: dir2 =& uffixBBBB (not ok)
fmt.Println("new path =&",string(path))
It didn't work as you expected. Instead of "AAAAsuffix/BBBBBBBBB" you ended up with "AAAAsuffix/uffixBBBB". It happened because both directory slices referenced the same underlying array data from the original path slice. This means that the original path is also modified. Depending on your application this might be a problem too.
This problem can fixed by allocating new slices and copying the data you need. Another option is to use the full slice expression.
package main
func main() {
path := []byte("AAAA/BBBBBBBBB")
sepIndex := bytes.IndexByte(path,'/')
dir1 := path[:sepIndex:sepIndex] //full slice expression
dir2 := path[sepIndex+1:]
fmt.Println("dir1 =&",string(dir1)) //prints: dir1 =& AAAA
fmt.Println("dir2 =&",string(dir2)) //prints: dir2 =& BBBBBBBBB
dir1 = append(dir1,"suffix"...)
path = bytes.Join([][]byte,[]byte{'/'})
fmt.Println("dir1 =&",string(dir1)) //prints: dir1 =& AAAAsuffix
fmt.Println("dir2 =&",string(dir2)) //prints: dir2 =& BBBBBBBBB (ok now)
fmt.Println("new path =&",string(path))
The extra parameter in the full slice expression controls the capacity for the new slice. Now appending to that slice will trigger a new buffer allocation instead of overwriting the data in the second slice.
"Stale" Slices
level: intermediate
Multiple slices can reference the same data. This can happen when you create a new slice from an existing slice, for example. If your application relies on this behavior to function properly then you'll need to worry about "stale" slices.
At some point adding data to one of the slices will result in a new array allocation when the original array can't hold any more new data. Now other slices will point to the old array (with old data).
import "fmt"
func main() {
s1 := []int
fmt.Println(len(s1),cap(s1),s1) //prints 3 3 [1 2 3]
s2 := s1[1:]
fmt.Println(len(s2),cap(s2),s2) //prints 2 2 [2 3]
for i := range s2 { s2[i] += 20 }
//still referencing the same array
fmt.Println(s1) //prints [1 22 23]
fmt.Println(s2) //prints [22 23]
s2 = append(s2,4)
for i := range s2 { s2[i] += 10 }
//s1 is now "stale"
fmt.Println(s1) //prints [1 22 23]
fmt.Println(s2) //prints [32 33 14]
Type Declarations and Methods
level: intermediate
When you create a type declaration by defining a new type from an existing (non-interface) type, you don't inherit the methods defined for that existing type.
错误信息:
package main
import "sync"
type myMutex sync.Mutex
func main() {
var mtx myMutex
mtx.Lock() //error
mtx.Unlock() //error
Compile Errors:
/tmp/sandbox/main.go:9: mtx.Lock undefined (type myMutex has no field or method Lock) /tmp/sandbox/main.go:10: mtx.Unlock undefined (type myMutex has no field or method Unlock)
If you do need the methods from the original type you can define a new struct type embedding the original type as an anonymous field.
修正代码:
package main
import "sync"
type myLocker struct {
sync.Mutex
func main() {
var lock myLocker
lock.Lock() //ok
lock.Unlock() //ok
Interface type declarations also retain their method sets.
修正代码:
package main
import "sync"
type myLocker sync.Locker
func main() {
var lock myLocker = new(sync.Mutex)
lock.Lock() //ok
lock.Unlock() //ok
Breaking Out of "for switch" and "for select" Code Blocks
level: intermediate
A "break" statement without a label only gets you out of the inner switch/select block. If using a "return" statement is not an option then defining a label for the outer loop is the next best thing.
package main
import "fmt"
func main() {
case true:
fmt.Println("breaking out...")
break loop
fmt.Println("out!")
A "goto" statement will do the trick too...
Iteration Variables and Closures in "for" Statements
level: intermediate
This is the most common gotcha in Go. The iteration variables in for statements are reused in each iteration. This means that each closure (aka function literal) created in your for loop will reference the same variable (and they'll get that variable's value at the time those goroutines start executing).
Incorrect:
package main
func main() {
data := []string{"one","two","three"}
for _,v := range data {
go func() {
fmt.Println(v)
time.Sleep(3 * time.Second)
//goroutines print: three, three, three
The easiest solution (that doesn't require any changes to the goroutine) is to save the current iteration variable value in a local variable inside the for loop block.
修正代码:
package main
func main() {
data := []string{"one","two","three"}
for _,v := range data {
vcopy := v //
go func() {
fmt.Println(vcopy)
time.Sleep(3 * time.Second)
//goroutines print: one, two, three
Another solution is to pass the current iteration variable as a parameter to the anonymous goroutine.
修正代码:
package main
func main() {
data := []string{"one","two","three"}
for _,v := range data {
go func(in string) {
fmt.Println(in)
time.Sleep(3 * time.Second)
//goroutines print: one, two, three
Here's a slightly more complicated version of the trap.
Incorrect:
package main
type field struct {
name string
func (p *field) print() {
fmt.Println(p.name)
func main() {
data := []field{{"one"},{"two"},{"three"}}
for _,v := range data {
go v.print()
time.Sleep(3 * time.Second)
//goroutines print: three, three, three
修正代码:
package main
type field struct {
name string
func (p *field) print() {
fmt.Println(p.name)
func main() {
data := []field{{"one"},{"two"},{"three"}}
for _,v := range data {
go v.print()
time.Sleep(3 * time.Second)
//goroutines print: one, two, three
What do you think you'll see when you run this code (and why)?
package main
type field struct {
name string
func (p *field) print() {
fmt.Println(p.name)
func main() {
data := []*field{{"one"},{"two"},{"three"}}
for _,v := range data {
go v.print()
time.Sleep(3 * time.Second)
Deferred Function Call Argument Evaluation
level: intermediate
Arguments for a deferred function call are evaluated when the defer statement is evaluated (not when the function is actually executing).
package main
import "fmt"
func main() {
var i int = 1
defer fmt.Println("result =&",func() int { return i * 2 }())
//prints: result =& 2 (not ok if you expected 4)
Deferred Function Call Execution
level: intermediate
The deferred calls are executed at the end of the containing function and not at the end of the containing code block. It's an easy mistake to make for new Go developers confusing the deferred code execution rules with the variable scoping rules. It can become a problem if you have a long running function with a for loop that tries to defer resource cleanup calls in each iteration.
package main
"path/filepath"
func main() {
if len(os.Args) != 2 {
os.Exit(-1)
start, err := os.Stat(os.Args[1])
if err != nil || !start.IsDir(){
os.Exit(-1)
var targets []string
filepath.Walk(os.Args[1], func(fpath string, fi os.FileInfo, err error) error {
if err != nil {
return err
if !fi.Mode().IsRegular() {
return nil
targets = append(targets,fpath)
return nil
for _,target := range targets {
f, err := os.Open(target)
if err != nil {
fmt.Println("bad target:",target,"error:",err) //prints error: too many open files
defer f.Close() //will not be closed at the end of this code block
//do something with the file...
One way to solve the problem is by wrapping the code block in a function.
package main
"path/filepath"
func main() {
if len(os.Args) != 2 {
os.Exit(-1)
start, err := os.Stat(os.Args[1])
if err != nil || !start.IsDir(){
os.Exit(-1)
var targets []string
filepath.Walk(os.Args[1], func(fpath string, fi os.FileInfo, err error) error {
if err != nil {
return err
if !fi.Mode().IsRegular() {
return nil
targets = append(targets,fpath)
return nil
for _,target := range targets {
f, err := os.Open(target)
if err != nil {
fmt.Println("bad target:",target,"error:",err)
defer f.Close() //ok
//do something with the file...
Another option is to get rid of the defer statement :-)
Failed Type Assertions
level: intermediate
Failed type assertions return the "zero value" for the target type used in the assertion statement. This can lead to unexpected behavior when it's mixed with variable shadowing.
Incorrect:
package main
import "fmt"
func main() {
var data interface{} = "great"
if data, ok := data.(int); ok {
fmt.Println("[is an int] value =&",data)
fmt.Println("[not an int] value =&",data)
//prints: [not an int] value =& 0 (not "great")
修正代码:
package main
import "fmt"
func main() {
var data interface{} = "great"
if res, ok := data.(int); ok {
fmt.Println("[is an int] value =&",res)
fmt.Println("[not an int] value =&",data)
//prints: [not an int] value =& great (as expected)
Blocked Goroutines and Resource Leaks
level: intermediate
Rob Pike talked about a number of fundamental concurrency patterns in his "Go Concurrency Patterns" presentation at Google I/O in 2012. Fetching the first result from a number of targets is one of them.
func First(query string, replicas ...Search) Result {
c := make(chan Result)
searchReplica := func(i int) { c &- replicas }
for i := range replicas {
go searchReplica(i)
return &-c
The function starts a goroutines for each search replica. Each goroutine sends its search result to the result channel. The first value from the result channel is returned.
What about the results from the other goroutines? What about the goroutines themselves?
The result channel in the First() function is unbuffered. This means that only the first goroutine returns. All other goroutines are stuck trying to send their results. This means if you have more than one replica each call will leak resources.
To avoid the leaks you need to make sure all goroutines exit. One potential solution is to use a buffered result channel big enough to hold all results.
func First(query string, replicas ...Search) Result {
c := make(chan Result,len(replicas))
searchReplica := func(i int) { c &- replicas }
for i := range replicas {
go searchReplica(i)
return &-c
Another potential solution is to use a select statement with a default case and a buffered result channel that can hold one value. The default case ensures that the goroutines don't get stuck even when the result channel can't receive messages.
func First(query string, replicas ...Search) Result {
c := make(chan Result,1)
searchReplica := func(i int) {
case c &- replicas:
for i := range replicas {
go searchReplica(i)
return &-c
You can also use a special cancellation channel to interrupt the workers.
func First(query string, replicas ...Search) Result {
c := make(chan Result)
done := make(chan struct{})
defer close(done)
searchReplica := func(i int) {
case c &- replicas:
case &- done:
for i := range replicas {
go searchReplica(i)
return &-c
Why did the presentation contain these bugs? Rob Pike simply didn't want to comlicate the slides. It makes sense, but it can be a problem for new Go developers who would use the code as is without thinking that it might have problems.
Using Pointer Receiver Methods On Value Instances
level: advanced
It's OK to call a pointer receiver method on a value as long as the value is addressable. In other words, you don't need to have a value receiver version of the method in some cases.
Not every variable is addressable though. Map elements are not addressable. Variables referenced through interfaces are also not addressable.
package main
import "fmt"
type data struct {
name string
func (p *data) print() {
fmt.Println("name:",p.name)
type printer interface {
func main() {
d1 := data{"one"}
d1.print() //ok
var in printer = data{"two"} //error
in.print()
m := map[string]data {"x":data{"three"}}
m["x"].print() //error
Compile Errors:
/tmp/sandbox/main.go:21: cannot use data literal (type data) as type printer in assignment: data does not implement printer (print method has pointer receiver)
/tmp/sandbox/main.go:25: cannot call pointer method on m["x"] /tmp/sandbox/main.go:25: cannot take the address of m["x"]
Updating Map Value Fields
level: advanced
If you have a map of struct values you can't update individual struct fields.
错误信息:
package main
type data struct {
name string
func main() {
m := map[string]data {"x":{"one"}}
m["x"].name = "two" //error
错误信息:
/tmp/sandbox/main.go:9: cannot assign to m["x"].name
It doesn't work because map elements are not addressable.
What can be extra confusing for new Go devs is the fact that slice elements are addressable.
package main
import "fmt"
type data struct {
name string
func main() {
s := []data {{"one"}}
s[0].name = "two" //ok
fmt.Println(s) //prints: []
Note that a while ago it was possible to update map element fields in one of the Go compilers (gccgo), but that behavior was quickly fixed :-) It was also considered as a potential feature for Go 1.3. It wasn't important enough to support at that point in time, so it's still on the todo list.
The first work around is to use a temporary variable.
package main
import "fmt"
type data struct {
name string
func main() {
m := map[string]data {"x":{"one"}}
r := m["x"]
r.name = "two"
m["x"] = r
fmt.Printf("%v",m) //prints: map[x:]
Another workaround is to use a map of pointers.
package main
import "fmt"
type data struct {
name string
func main() {
m := map[string]*data {"x":{"one"}}
m["x"].name = "two" //ok
fmt.Println(m["x"]) //prints: &
By the way, what happens when you run this code?
package main
type data struct {
name string
func main() {
m := map[string]*data {"x":{"one"}}
m["z"].name = "what?" //???
"nil" Interfaces and "nil" Interfaces Values
level: advanced
This is the second most common gotcha in Go because interfaces are not pointers even though they may look like pointers. Interface variables will be "nil" only when their type and value fields are "nil".
The interface type and value fields are populated based on the type and value of the variable used to create the corresponding interface variable. This can lead to unexpected behavior when you are trying to check if an interface variable equals to "nil".
package main
import "fmt"
func main() {
var data *byte
var in interface{}
fmt.Println(data,data == nil) //prints: &nil& true
fmt.Println(in,in == nil) //prints: &nil& true
fmt.Println(in,in == nil) //prints: &nil& false
//'data' is 'nil', but 'in' is not 'nil'
Watch out for this trap when you have a function that returns interfaces.
Incorrect:
package main
import "fmt"
func main() {
doit := func(arg int) interface{} {
var result *struct{} = nil
if(arg & 0) {
result = &struct{}{}
翻译一半我也是醉了还不如不翻译
又在说我坏话
加班没时间,翻译起来是要人命的工作。但是你要是有啥问题的话,可以直接在Go 区提问,我尽力帮你
@大舒 只是吐槽一下233333
明天提醒我
我要该,理由是:
关闭理由:
删除理由:
忽略理由:
推广(招聘、广告、SEO 等)方面的内容
与已有问题重复(请编辑该提问指向已有相同问题)
答非所问,不符合答题要求
宜作评论而非答案
带有人身攻击、辱骂、仇恨等违反条款的内容
无法获得确切结果的问题
非开发直接相关的问题
非技术提问的讨论型问题
其他原因(请补充说明)
扫扫下载 App

我要回帖

更多关于 component注解 的文章

 

随机推荐