为什么c语言if else语句中的go?to 语句老师说要小心使用?

Go,怎么的要使用,语言?Go,语言的优势在哪里_编程_阁子数码知识网
Go,怎么的要使用,语言?Go,语言的优势在哪里
编辑: 阁子数码知识网 &&&来源:用户发布&&&发布时间:&&&查看次数:29
Go,怎么的要使用,语言?Go吒呀,语言的优势在哪里,郁闷了。
【探讨解答】
为什么要使用Go语言,Go语言的优势在哪里
我尝试来回答你几个问题: 1、Go有什么优势 可直接编译成机器码,不依赖其他库,glibc的版本有一定要求,部署就是扔一个文件上去就完成了。 静态类型语言,但是有动态语言的感觉,静态类型的语言就是可以在编译的时候检查出来隐藏的大多数问题,...
为什么C语言中的goto语句老师说要小心使用?注意...
goto语句称为无条件转移语句,通常与条件语句配合使用。可用来实现条件转移, 构成循环,跳出循环体等功能。但是,在结构化程序设计中一般不主张使用goto语句, 以免造成程序流程的混乱,使理解和调试程序都产生困难。而且,goto语句一般可以用...
更多相关内容
本站内容来自网友发布,本站无法保证其部分内容的正确性,请用户一定仔细辨别。
[] &&[联系QQ:885&971&98] &
沪ICP备号&go+to语句,go+to语句,goto语句的用法,goto语句怎么用,goto语句是什么,goto语句与标号的关系如何,goto语句只能用于退出多层循环,goto语句的使用,vba goto语句,c语言goto语句,c#goto语句,java goto语句_知识库
java用( )来实现go to语句所特有的一些功能。 a.breakb.def......
unit2 what time do you go to school重要短语和句子_英语_初中教育_教育专区。罗列a b部分重要短语和句型 还配有习题unit2 what time do you go to school.........
java用( )来实现go to语句所特有的一些功能。 a.breakb.def......
六年级上册_unit2 第一课时ways to go to school ets_learn课件_英语_小学教育_教育专区。pep六年级上册unit2 ways to go to school课件 .........
在某一帧暂停:go to the frame 跳转到某个影片:play movie “**” 在第一...(继续) on rightmouseup me go previous(去前一个) end 倒数计时器的语句(........
6、i’ll go and join them.(改否定) i ___ go ___ join them. 7、i’m going to get up at 6:30 tomorrow.(改一般疑问句) ___ ___ ___ .........
unit3 how do you go to school_英语_初中教育_教育专区 暂无评价|0人阅读|0次下载|举报文档unit3 how do you go to school_英语_初中教育_教育专区。全.........
else if (逻辑表达式 n) then 程序块 n else 程序块 n+1 end if 7 fortran77 循环语句 1 go to 语句 标号程序行 程序块 go to 标号 2 do 语句.........
who would you like to have___( go ) ? who would you like to have___(go) with? what song did you hear ___( sing )? we saw the house _.........
else if (逻辑表达式 n) then 程序块 n else 4 fortran77 指导手册程序块 n+1 end if 7 fortran77 循环语句 1 go to 语句 标号程序行 程序块 go to.........
won t will not buy be going to 练习 i.选择适当的词填空。
a: what is she___(do)? b: she___(cook)dinner in the kitchen .........
七年级下册课本重点短语和句型 unit 2 what time do you go to school_初一英语_英语_初中教育_教育专区。unit 2 what time do you go to school? 重点短语.........
go to statement considered harmful edsger w.dijkstra interpreter:sdulmz :经过数年的观察我发现:编程人员的质量是其在程序中产生的 go to 语句密度的减函数。.........
四。转移语句(go to) 当需要使程序改变正常执行的顺序时,可以使用无条件转移语句 go to 。其一 般格式为: 格式 1: go to 过程名 格式 2: go to 过程名 .........
if-goto语句循环语句用法例题代码_工学_高等教育_教育专区。if-goto语句循环语句用法例题代码if-goto 语句循环语句用法例题代码 语句循环语句用法例题代码 *例题 ........
语句重音练习--to ss_英语学习_外语学习_教育专区。按照重音模式先给以下短语和...to go there| a dance tomorrow evening| he studies every evening| he .........
电视剧《生活向前冲》(go on)中的经典语句_计算机软件及应用_it计算机_专业资料...h:well,maybe this crazy genius finds your nervousness to be cute.(kiss).........
英语励志经典语句(最新)_初一英语_英语_初中教育_教育专区。英语励志经典语句 ...-- 卡莱尔 victory wont come to me unless i go to it. -- 胜利是不.........
how to + 动词原形 表示“如何做 某事”,是复合不定式结构......
逆读法:分钟为+介词 topast+钟点数,可分两种情况: 2 七年级 unit 2 what time do you go to school ? 姓名:___ 1)分钟为不超过半小时,用分钟数+past.........
■ 24小时热门信息
在页面的左侧或右侧,也可以放置在页面的顶端或 底端。可使用“文本框工具”选项卡更改提要栏文本框的格式。] 一章 绪论【学习目标】 了解 java 的发展历史.........
www. java 是什么 java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 sun microsystems 公司于 1995 年 5 月推出的 java 程序设计.........
java基础笔记_it计算机_专业资料。搭建java开发环境: jdkjrejvm 什么是jdk:java开发工具包(做java开发是必须下载jdk) 什么是jre:java的运行环境(若只运行java.........
java.lang.nullpointerexception 这个异常大家肯定都......
■ 相关热门内容
niit全球it教育领先者 http: niit java 实用教程 目录第1章 java环境及配置 第2章 java基本语法 第3章 类和接口 第4章 java applet 第........
java各知识点详细总结_计算机软件及应用_it计算机_专业资料。java知识点详细总结,每次看都有不同的收获!java 基础知识总结 写代码: 1,明确需求。我要做什么? ........
java基本知识学习(零基础哦)_计算机软件及应用_it计算机_专业资料。java基本知识学习(零基础哦) java基础 教师自我介绍 ?教师姓名: ?(请进行自我介绍 ) 课程.........
java 编译器才会找到该类 也可以使用 import 在文件的开头引入要使用的类,例:import com.yao.myclass 可以不需要用 import 语句直接使用 java.lang 包中的类 .........
() ___机制是 java 面向对象程序设计中实现软件可重 用性的最重要手段。 java 语言中 ___语句是实现多重选择的。 判断题 判断题 判断题 判断题 填空题 填.........
java中常用英语_英语学习_外语学习_教育专区。java 与英语干程序员这行实......
java作业一题目&答案_理学_高等教育_教育专区。1、 简述什么是 java 垃圾回收机制?有什么意义?列举 6 种 java 垃圾回收算法。 垃圾回收是一种动态存储管理技术,.........
java课程设计总结_工学_高等教育_教育专区。不过,还有很多知识我都不懂,比如......
■ 热门推荐sql语言中有没有类似C语言中的switch case的语句??case
sql语言中有没有类似C语言中的switch case的语句??& 没有,用case&& when&& 来代替就行了.&& &&&&&& & 例如,下面的语句显示中文年月&& &&& & select&& getdate()&& as&& 日期,case&& month(getdate())&& & when&& 11&& then&& '十一'&& & when&& 12&& then&& '十二'&& & else&& substring('一二三四五六七八九十',&& month(getdate()),1)&& & end+'月'&& as&& 月份=====================================================================CASE 可能是 SQL 中被误用最多的关键字之一。虽然你可能以前用过这个关键字来创建字段,但是它还具有更多用法。例如,你可以在 WHERE 子句中使用 CASE。
首先让我们看一下 CASE 的语法。在一般的 SELECT 中,其语法如下:
SELECT &myColumnSpec& = CASE WHEN &A& THEN &somethingA& WHEN &B& THEN &somethingB& ELSE &somethingE& END
在上面的代码中需要用具体的参数代替尖括号中的内容。下面是一个简单的例子:
USE pubs GO SELECT &&&& Title, &&& 'Price Range' = &&& CASE &&&&&&& WHEN price IS NULL THEN 'Unpriced' &&&&&&& WHEN price & 10 THEN 'Bargain' &&&&&&& WHEN price BETWEEN 10 and 20 THEN 'Average' &&&&&&& ELSE 'Gift to impress relatives' &&& END FROM titles ORDER BY price GO
这是 CASE 的典型用法,但是使用 CASE 其实可以做更多的事情。比方说下面的 GROUP BY 子句中的 CASE:
SELECT 'Number of Titles', Count(*) FROM titles GROUP BY &&& CASE &&&&&&& WHEN price IS NULL THEN 'Unpriced' &&&&&&& WHEN price & 10 THEN 'Bargain' &&&&&&& WHEN price BETWEEN 10 and 20 THEN 'Average' &&&&&&& ELSE 'Gift to impress relatives' &&& END GO
你甚至还可以组合这些选项,添加一个 ORDER BY 子句,如下所示:
USE pubs GO SELECT &&& CASE &&&&&&& WHEN price IS NULL THEN 'Unpriced' &&&&&&& WHEN price & 10 THEN 'Bargain' &&&&&&& WHEN price BETWEEN 10 and 20 THEN 'Average' &&&&&&& ELSE 'Gift to impress relatives' &&& END AS Range, &&&& Title FROM titles GROUP BY &&& CASE &&&&&&& WHEN price IS NULL THEN 'Unpriced' &&&&&&& WHEN price & 10 THEN 'Bargain' &&&&&&& WHEN price BETWEEN 10 and 20 THEN 'Average' &&&&&&& ELSE 'Gift to impress relatives' &&& END, &&&& Title ORDER BY &&& CASE &&&&&&& WHEN price IS NULL THEN 'Unpriced' &&&&&&& WHEN price & 10 THEN 'Bargain' &&&&&&& WHEN price BETWEEN 10 and 20 THEN 'Average' &&&&&&& ELSE 'Gift to impress relatives' &&& END, &&&& Title GO
注意,为了在 GROUP BY 块中使用 CASE,查询语句需要在 GROUP BY 块中重复 SELECT 块中的 CASE 块。
除了选择自定义字段之外,在很多情况下 CASE 都非常有用。再深入一步,你还可以得到你以前认为不可能得到的分组排序结果集。点击阅读原文
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}

我要回帖

更多关于 c语言switch case语句 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信