1、不容许左大括号独自一行
2、不容许呈现未应用的变量
3、不容许呈现未应用的import(应用 _ 包名
引入)
4、短的变量申明(Short Variable Declarations)只能在函数外部应用
// myvar := 1 // errorvar myvar = 1 // ok
5、不能应用短变量申明(Short Variable Declarations)反复申明
6、不能应用短变量申明(Short Variable Declarations)这种形式来设置字段值
data.result, err := work() //error
7、意外的变量幽灵(Accidental Variable Shadowing)
代码块中同名短变量申明从申明开始到代码块完结,对变量的批改将不会影响到内部变量!
8、不能应用nil初始化一个未指定类型的变量
9、不能间接应用nil值的Slice和Map
10、map应用make分配内存时可指定capicity,然而不能对map应用cap函数
在golang中,nil只能赋值给指针、channel、func、interface、map或slice类型的变量。
12、数组用于函数传参时是值复制
留神:办法或函数调用时,传入参数都是值复制(跟赋值统一),除非是map、slice、channel、指针类型这些非凡类型是援用传递。
13、range关键字返回是键值对,而不是值
14、Slice和Array是一维的
15、从不存在key的map中取值时,返回的总是”0值”
16、字符串是不可变的
17、字符串与[]byte之间的转换是复制(有内存损耗),能够用map[string] []byte建设字符串与[]byte之间映射,也可range来防止内存调配来进步性能
//[]byte: for i,v := range []byte(str) {}
18、string的索引操作返回的是byte(或uint8),如想获取字符可应用for range,也可应用unicode/utf8包和golang.org/x/exp/utf8string包的At()办法。
19、字符串并不总是UTF8的文本
20、len(str)返回的是字符串的字节数
str := "我"fmt.Println(len(str)) //3
21、在Slice、Array、Map的多行书写最初的逗号不可省略,单行书写,最初一个元素的逗号可省略
22、内置数据结构的操作并不同步,但可把Go提供了并发的个性应用起来:goroutines和channels。
23、应用for range迭代String,是以rune来迭代的。
一个字符,也能够有多个rune组成。须要解决字符,尽量应用golang.org/x/text/unicode/norm包。
for range总是尝试将字符串解析成utf8的文本,对于它无奈解析的字节,它会返回oxfffd的rune字符。
因而,任何蕴含非utf8的文本,肯定要先将其转换成字符切片([]byte)。
24、应用for range迭代map时每次迭代的程序可能不一样,因为map的迭代是随机的。
25、switch的case默认匹配规定不同于其它语言的是,匹配case条件后默认退出,除非应用fallthrough持续匹配;而其它语言是默认持续匹配,除非应用break退出匹配。
26、只有后置自增(a++
)、后置自减,不存在前置自增(++a
)、前置自减
27、位运算的非操作是^(跟异或位运算一样),有别于其它语言的~。
28、位运算(与、或、异或、取反)优先级高于四则运算(加、减、乘、除、取余),有别于C语言。
29、构造体在序列化时非导出字段(以小写字母结尾的字段名)不会被encode,因而在decode时这些非导出字段的值为”0值”
30、程序不等所有goroutine完结就会退出。可通过channel实现主协程(main goroutine)期待所有goroutine实现。
31、对于无缓存区的channel,写入channel的goroutine会阻塞直到被读取,读取channel的goroutine会阻塞直到有数据写入。
32、从一个closed状态的channel读取数据是平安的,可通过返回状态(第二个返回参数)判断是否敞开;而向一个closed状态的channel写数据会导致panic。
33、向一个nil值(未用make调配空间)的channel发送或读取数据,会导致永远阻塞。
34、办法接收者是类型(T),接收者只是原对象的值复制,在办法中批改接收者不会批改原始对象的值;如果办法接收者是指针类型(*T),是对原对象的援用,办法中对其批改当然是原对象批改。
35、log包中的log.Fatal和log.Panic不仅仅记录日志,还会停止程序。它不同于Logging库。
36、应用defer语句敞开资源时要留神nil值,在defer语句之前要进行nil值判断解决(否则会引发空援用的panic)
37、敞开HTTP连贯,可应用
- req.Close=true,示意在http申请实现时敞开连贯
- 增加Connection: close的连贯申请头。http服务端也会发送Connection: close的响应头,http库解决响应时会敞开连贯。
全局敞开http连贯重用。
package mainimport ( "fmt" "net/http" "io/ioutil")func main() { //全局敞开http连贯重用 //tr := &http.Transport{DisableKeepAlives: true} //client := &http.Client{Transport: tr} req, err := http.NewRequest("GET","http://golang.org",nil) if err != nil { fmt.Println(err) return } 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) return } body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) return } fmt.Println(len(string(body)))}
37、Json反序列化数字到interface{}类型的值中,默认解析为float64类型
38.Struct、Array、Slice、Map的比拟
如果struct构造体的所有字段都可能应用==操作比拟,那么构造体变量也可能应用==比拟。
然而,如果struct字段不能应用==比拟,那么构造体变量应用==比拟会导致编译谬误。
同样,array只有在它的每个元素可能应用==比拟时,array变量才可能比拟。
Go提供了一些用于比拟不能间接应用==比拟的函数,其中最罕用的是reflect.DeepEqual()函数。
DeepEqual()函数对于nil值的slice与空元素的slice是不相等的,这点不同于bytes.Equal()函数。
var b1 []byte = nilb2 := []byte{}fmt.Println("b1 == b2:",reflect.DeepEqual(b1, b2)) //prints: b1 == b2: falsevar b3 []byte = nilb4 := []byte{}fmt.Println("b3 == b4:",bytes.Equal(b3, b4)) //prints: b3 == b4: true
如果要疏忽大小写来比拟蕴含文字数据的字节切片(byte slice),
不倡议应用bytes包和strings包里的ToUpper()、ToLower()这些函数转换后再用==、byte.Equal()、bytes.Compare()等比拟,ToUpper()、ToLower()只能解决英文文字,对其它语言有效。因而倡议应用strings.EqualFold()和bytes.EqualFold()
如果要比拟用于验证用户数据密钥信息的字节切片时,应用reflact.DeepEqual()、bytes.Equal()、
bytes.Compare()会使应用程序蒙受计时攻打(Timing Attack),可应用crypto/subtle.ConstantTimeCompare()防止透露工夫信息。
39、recover()函数可能捕捉或拦挡panic,但必须在defer函数或语句中间接调用,否则有效。
40、在slice、array、map的for range获取的数据项是从汇合元素的复制过去的,并非援用原始数据,但应用索引能拜访原始数据。
data := []int{1,2,3}for _,v := range data { v *= 10 // original item is not changed}data2 := []int{1,2,3}for i,v := range data2 { data2[i] *= 10 // change original item}// 元素是指针类型就不一样了data3 := []*struct{num int} {{1}, {2}, {3}}for _,v := range data { v.num *= 10}fmt.Println("data:", data) //prints data: [1 2 3]fmt.Println("data:", data2) //prints data: [10 20 30]fmt.Println(data3[0],data3[1],data3[2]) //prints &{10} &{20} &{30}
41、从一个slice上再生成一个切片slice,新的slice将间接援用原始slice的那个数组,两个slice对同一数组的操作,会相互影响。
可通过copy()为新切片slice重新分配空间,从slice中copy局部的数据来防止相互之间的影响。
42.从已存在的切片slice中持续切片时,新切片的capicity等于原capicity减去新切片之前局部的数量,新切片与原切片都指向同一数组空间。
新生成切片之间capicity区域是重叠的,因而在增加数据时易造成数据笼罩问题。
slice应用append增加的内容时超出capicity时,会重新分配空间。
利用这一点,将要批改的切片指定capicity为切片以后length,可防止切片之间的超范围笼罩影响。
path := []byte("AAAA/BBBBBBBBB") sepIndex := bytes.IndexByte(path,'/') dir1 := path[:sepIndex] // 解决办法 // 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{dir1,dir2},[]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))
43、slice在增加元素前,与其它切片共享同一数据区域,批改会相互影响;但增加元素导致内存重新分配之后,不再指向原来的数据区域,批改元素,不再影响其它切片。
s1 := []int{1,2,3} 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]
44、类型重定义与办法继承
从一个已存在的(non-interface)非接口类型从新定义一个新类型时,不会继承原类型的任何办法。
能够通过定义一个组合匿名变量的类型,来实现对此匿名变量类型的继承。
然而从一个已存在接口从新定义一个新接口时,新接口会继承原接口所有办法。
45、从”for switch”和”for select”代码块中跳出。
无label的break只会跳出最内层的switch/select代码块。
如须要从switch/select代码块中跳出外层的for循环,能够在for循环内部定义label,供break跳出。
return当然也是能够的,如果在这里能够用的话。
46、在for语句的闭包中应用迭代变量会有问题
在for迭代过程中,迭代变量会始终保留,只是每次迭代值不一样。
因而在for循环中在闭包里间接援用迭代变量,在执行时间接取迭代变量的值,而不是闭包所在迭代的变量值。
如果闭包要取所在迭代变量的值,就须要for中定义一个变量来保留所在迭代的值,或者通过闭包函数传参。
47、defer函数调用参数
defer前面必须是函数或办法的调用语句。defer前面不论是函数还是办法,输出参数的值是在defer申明时已计算好,
而不是调用开始计算。
要特地留神的是,defer前面是办法调用语句时,办法的接受者是在defer语句执行时传递的,而不是defer申明时传入的。
48、defer语句调用是在以后函数完结之后调用,而不是变量的作用范畴。
49、失败的类型断言:var.(T)类型断言失败时会返回T类型的“0值”,而不是变量原始值。
func main() { var data interface{} = "great" res, ok := data.(int); fmt.Println("res =>",res, ",ok =>",ok)//res => 0 ,ok => false}
50、阻塞的goroutine与资源透露
func First(query string, replicas ...Search) Result { c := make(chan Result) // 解决1:应用缓冲的channel: c := make(chan Result,len(replicas)) searchReplica := func(i int) { c <- replicas[i](query) } // 解决2:应用select-default,避免阻塞 // searchReplica := func(i int) { // select { // case c <- replicas[i](query): // default: // } // } // 解决3:应用非凡的channel来中断原有工作 // done := make(chan struct{}) // defer close(done) // searchReplica := func(i int) { // select { // case c <- replicas[i](query): // case <- done: // } // } for i := range replicas { go searchReplica(i) } return <-c}
51、用值实例上调用接收者为指针的办法
对于可寻址(addressable)的值变量(而不是指针),能够间接调用承受对象为指针类型的办法。
换句话说,就不须要为可寻址值变量定义以承受对象为值类型的办法了。
然而,并不是所有变量都是可寻址的,像Map的元素就是不可寻址的。
package mainimport "fmt"type data struct { name string}func (p *data) print() { fmt.Println("name:",p.name)}type printer interface { print()}func main() { d1 := data{"one"} d1.print() //ok // var in printer = data{"two"} //error var in printer = &data{"two"} in.print() m := map[string]data {"x":data{"three"}} //m["x"].print() //error d2 = m["x"] d2.print() // ok}
52、更新map值的字段
如果map的值类型是构造体类型,那么不能更新从map中取出的构造体的字段值。
然而对于构造体类型的slice却是能够的。
package maintype data struct { name string}func main() { m := map[string]data {"x":{"one"}} //m["x"].name = "two" //error r := m["x"] r.name = "two" m["x"] = r fmt.Println(s) // prints: map[x:{two}] mp := map[string]*data {"x": {"one"}} mp["x"].name = "two" // ok s := []data{{"one"}} s[0].name = "two" // ok fmt.Println(s) // prints: [{two}]}
53、nil值的interface{}不等于nil
在golang中,nil只能赋值给指针、channel、func、interface、map或slice类型的变量。
interface{}示意任意类型,能够接管任意类型的值。interface{}变量在底是由类型和值两局部组成,示意为(T,V),interface{}变量比拟非凡,判断它是nil时,要求它的类型和值都是nil,即(nil, nil)。
其它类型变量,只有值是nil,那么此变量就是nil(为什么?变量类型不是nil,那当然只能用值来判断了)
申明变量interface{},它默认就是nil,底层类型与值示意是(nil, nil)。
当任何类型T的变量值V给interface{}变量赋值时,interface{}变量的底层示意是(T, V)。只有T非nil,即便V是nil,interface{}变量也不是nil。
var data *byte var in interface{} fmt.Println(data,data == nil) //prints: <nil> true fmt.Println(in,in == nil) //prints: <nil> true in = data fmt.Println(in,in == nil) //prints: <nil> false //'data' is 'nil', but 'in' is not 'nil' doit := func(arg int) interface{} { var result *struct{} = nil if(arg > 0) { result = &struct{}{} } return result } if res := doit(-1); res != nil { fmt.Println("good result:",res) //prints: good result: <nil> //'res' is not 'nil', but its value is 'nil' } doit = func(arg int) interface{} { var result *struct{} = nil if(arg > 0) { result = &struct{}{} } else { return nil //return an explicit 'nil' } return result } if res := doit(-1); res != nil { fmt.Println("good result:",res) } else { fmt.Println("bad result (res is nil)") //here as expected }
54、变量内存的调配
在C++中应用new操作符总是在heap上调配变量。Go编译器应用new()和make()分配内存的地位到底是stack还是heap,
取决于变量的大小(size)和逃逸剖析的后果(result of “escape analysis”)。这意味着Go语言中,返回本地变量的援用也不会有问题。
要想晓得变量内存调配的地位,能够在go build、go run命令指定-gcflags -m即可:
go run -gcflags -m app.go
55、GOMAXPROCS、Concurrency和Parallelism
Go 1.4及以下版本每个操作系统线程只应用一个执行上下文execution context)。这意味着每个工夫片,只有一个goroutine执行。
从Go 1.5开始能够设置执行上下文的数量为CUP内核数量runtime.NumCPU(),也能够通过GOMAXPROCS环境变量来设置,
还可调用runtime.GOMAXPROCS()函数来设置。
留神,GOMAXPROCS并不代表Go运行时可能应用的CPU数量,它是一个小256的数值,能够设置比理论的CPU数量更大的数字。
56、读写操作排序
Go可能会对一些操作排序,但它保障在goroutine的所有行为放弃不变。
然而,它无奈保障在跨多个goroutine时的执行程序。
package mainimport ( "runtime" "time")var _ = runtime.GOMAXPROCS(3)var a, b intfunc u1() { a = 1 b = 2}func u2() { a = 3 b = 4}func p() { println(a) println(b)}func main() { go u1() go u2() go p() time.Sleep(1 * time.Second) // 屡次执行可显示以下以几种打印后果 // 1 2 // 3 4 // 0 2 (奇怪吗?) // 0 0 // 1 4 (奇怪吗?)}
57、优先调度
有一些比拟流氓的goroutine会阻止其它goroutine的执行。
例如for循环可能就不容许调度器(scheduler)执行。
scheduler会在GC、go语句、阻塞channel的操作、阻塞零碎调用、lock操作等语句执行之后立刻执行。
也能够显示地执行runtime.Gosched()(让出工夫片)使scheduler执行调度工作。
package mainimport ( "fmt" "runtime")func main() { done := false go func(){ done = true }() for !done { // ... //runtime.Gosched() // 让scheduler执行调度,让出执行工夫片 } fmt.Println("done!")}
参考资料:https://blog.csdn.net/gezhong...