-
使用定时器
t := time.NewTicker(1 * time.Second) // 第一种方式 for { select { case <-t.C: fmt.Println("hello ") default: time.Sleep(100 * time.Millisecond) } } // 第二种方式 for _ = range t.C { fmt.Println("hello") }
-
生成指定长度的随机字符串
rand.Seed(time.Now().UnixNano()) data := make([]byte, 32) rand.Read(data) fmt.Println(hex.EncodeToString(data))
-
slice转array
func main() { var arr [10]int s := []int{1, 2, 3, 4, 5} copy(arr[:], s) fmt.Println(arr) }
原理解析:arr[:]是一个引用了底层数组arr的slice,长度和容量与数组的长度相同,也就是说这两个元素共用一块内存。所以当copy()时,改变了slice中的元素值,而数组的改变可以说是一种副效应(side-effect)。
-
通过append函数删除slice中的元素,是否会重新分配内存的问题
package main import "fmt" func main() { s := []int{1, 2, 3, 4, 5} a := s fmt.Println(a) s = append(s[:1], s[2:]...) // 修改s[0],如果a的数据也变化了,那么说明通过append() // 删除slice中的数据并没有重新分配内存空间 s[0] = 12 fmt.Println(a, s) } // output [1 2 3 4 5] [6 3 4 5 5] [6 3 4 5]
-
交换两个元素的值,而不需要中间变量
package main import "fmt" func main() { i := 1 j := 2 i = i ^ j j = i ^ j i = i ^ j fmt.Println(i, j) } // 规律是 i ^ i = 0
-
如果当前函数或者其调用栈上游,有处理panic的recover(),panic()就不会输出,更不会终止程序,但是panic()的确执行了。
package main import "fmt" func main() { test() fmt.Println("here is waiting") } func test() { defer func() { // 可以在panic()上游的任何位置定义,都会拦截 if r := recover(); r != nil { fmt.Println("is there") } }() base() } func base() { panic("this is a error") }
-
从一个给定的slice获取空的slice
package main import "fmt" func main() { s := []int{1, 2, 3, 4, 5} sEmpty := s[0:0] fmt.Println(sEmpty) }
-
[]byte转十六进制
b := []byte{12, 34, 89} // 以下会得到相同的输出 fmt.Printf("%x\n", b) fmt.Println(hex.EncodeToString(b)
-
大端法与小端法
给定一个十六进制数0xOA0B0C0D,以下给出了在两种不同方法下内存的排序方式,内存地址从左向右依次增大。上方为小端法表示、下方为大端法表示。
概念:
- 大端法: 数字的高位和低位与内存地址相反
- 小端法: 数字的高位和低位与内存地址相同
比较好记的是大端法数字的书写顺序与内存排列顺序一致
![大端法、小端法][image-1]
在golang中将一个数字转换成byte,在小端法中,会解析低内存地址的一个byte,作为输出.
func main() { var a uint32 = 0x0A0B0C0D fmt.Println(byte(a)) }
-
slice copy时注意事项
第一种情况为空slice,因此在使用Copy()方法时,目标slice长度一定要指定为需要copy的长度
func main() { s := make([]int, 0, 5) // error //s := make([]int, 4) // correct 4 or 5 ok c := []int{1, 2, 3, 4, 5} copy(s, c) fmt.Println(s) }
-
标识符冲突
同一个包内不能出现变量名、常量名、函数名两两冲突的情况,如下情况编译失败:
package main import "fmt" const conflict = 38 var conflict = 34 func conflict() { fmt.Println(conflict) } func main() { fmt.Println(conflict) conflict() }
-
求两个时间的间隔,会参数负数的情况
func main() { t1 := time.Now() t2 := time.Now() fmt.Println(t1.Sub(t2)) // 负数 t3 := time.Unix(math.MaxInt64, 0) // 属于越界的情况 fmt.Println(t3.Sub(t1)) } // output -657ns -2562047h47m16.854775808s
所以在使用time包的Sub()函数时,要将结果与0进行比较
now := time.Now()
lastAttempt := now.Sub(ka.lastattempt) // 如果差值过大,就会形成负值if lastAttempt < 0 { lastAttempt = 0 }
-
nil slice可以直接使用append()增加元素
func main() { var b []byte source := []byte{1, 2, 3} b = append(b, source...) fmt.Println(b) }
-
golang set循环控制及返回值
func (ba *BlockAssembler) isStillDependent(te *mempool.TxMempoolEntry) bool { setParent := blockchain.GMemPool.GetMemPoolParents(te) ret := false setParent.Each(func(item interface{}) bool { if !ba.inBlock.Has(item.(*mempool.TxMempoolEntry)) { ret = true // control function result return false // control loop } return true // control loop }) return ret // return function result }
-
select默认是堵塞的,必须有一个分支执行才能出去。
func main() { a := make(chan int, 1) c := make(chan int, 1) go func() { time.Sleep(1 * time.Second) a <- 1 c <- 2 }() // for next { select { case <-a: fmt.Println("a") case <-c: fmt.Println("c") } } // 如果所有通道中都没有内容,可以使用default语句,继续程序的执行。不过要看业务需求。 func main() { a := make(chan int, 1) c := make(chan int, 1) go func() { time.Sleep(1 * time.Second) a <- 1 c <- 2 }() // for next { select { case <-a: fmt.Println("a") case <-c: fmt.Println("c") default: fmt.PrintLn("default") } }
-
switch结构中default语句的执行与其所在位置无关
package main import "fmt" func main() { a := 232 switch a { default: fmt.Println("default") case 1: fmt.Println(1) case 232: fmt.Println("ok") } }
-
遍历一个无缓冲的channel,不会在从channel中读取一个元素就退出(for range 结构); for range 的终止条件是通道关闭,和channel有无缓冲无关。
package main import ( "fmt" "time" ) func main() { c := make(chan int) go func() { i := 0 for { c <- i i++ time.Sleep(1 * time.Second) } }() go func() { for v := range c { fmt.Println(v) } }() time.Sleep(10 * time.Second) close(c) }
-
len()函数在channel的应用。当chanel为无缓冲的时候,len()在任何情况下都会返回0; 当channel为有缓冲的情况下,len()会返回当前缓冲中的元素个数,增大或减小取决于生产者和消费者的速度。总之,len()函数在channel中返回的是缓冲区的元素个数。
package main import ( "fmt" "time" ) func main() { c := make(chan int, 5) // 修改为无缓冲的channel尝试一下 // producer go func() { i := 0 for { c <- i i++ time.Sleep(1 * time.Second) } }() // consumer go func() { for v := range c { fmt.Println(v) time.Sleep(2 * time.Second) } }() // watcher go func() { for { fmt.Println("channel length:", len(c)) time.Sleep(500 * time.Millisecond) } }() time.Sleep(30 * time.Second) }
-
goto、continue、break语句实现代码跳转
// 既可以往前跳,也可以往后跳 func main() { previous: fmt.Println("previous") i := 0 if i == 0 { goto next } next: fmt.Println("next") goto previous } // break跳转至指定标签后,将不执行标签对应的for循环。需要注意的是:标签只能定义在for循环之前。 func main() { i := 0 loop: for i <= 10 { i++ fmt.Println("break") break loop } fmt.Println("finish, and i ==", i) } // output: break finish, and i == 1 // continue跳转到指定标签后,如果for循环条件满足仍然会执行for循环,直到条件不满足为止。需要注意的是:标签只能定义在for循环之前。 func main() { i := 0 loop: for i <= 10 { i++ fmt.Println("continue") continue loop } fmt.Println("finish, and i ==", i) } // output: continue continue continue continue continue continue continue continue continue continue continue finish, and i == 11
-
switch case for type asert:
func main() { var i interface{} a := 3 i = a switch t := i.(type) { case int: fmt.Println(t) default: fmt.Println("default") } } //output: 3
-
<- c
被触发的条件:1、通道中有数据
2、通道被关闭
func main() { c := make(chan struct{}) //c := make(chan struct{}, 100) go func() { time.Sleep(5 * time.Second) close(c) }() for { if interruptRequest(c) { break } time.Sleep(1 * time.Second) } } func interruptRequest(interrupted <-chan struct{}) bool { select { case <-interrupted: fmt.Println("program is down....") return true default: fmt.Println("program is running...") } return false }
-
函数内对slice作append,在函数外不可见: slice的元信息是SliceHeader,slice按值传递和按引用传递其实就是将SliceHeader按值传递或按指针传递。
func main() { s := make([]int, 0) a := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", a) test(s) b := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", b) } func test(s []int) { s = append(s, 1) a := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", a) } // output: &{Data:18193560 Len:0 Cap:0} &{Data:842350567624 Len:1 Cap:1} &{Data:18193560 Len:0 Cap:0} // ----------------------------------------------------- // 通过传slice指针,实现对slice的修改 func main() { s := make([]int, 0) a := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", a) test(&s) b := (*reflect.SliceHeader)(unsafe.Pointer(&s)) fmt.Printf("%+v\n", b) } func test(s *[]int) { *s = append(*s, 1) a := (*reflect.SliceHeader)(unsafe.Pointer(s)) fmt.Printf("%+v\n", a) } // output: &{Data:18193560 Len:0 Cap:0} &{Data:842350567624 Len:1 Cap:1} &{Data:842350567624 Len:1 Cap:1}
-
json自定义序列化
对于使用结构体中嵌套结构体的情况,只有receiver为指针类型,而嵌套结构体为结构体的值语义的时候不能触发自定义Json格式化函数MarshalJSON;其他三种组合均能够触发。
对于使用结构体中嵌套结构体slice的情况,receiver值语义、指针语义和嵌套结构体slice元素为值语义、指针语义的四种组合均能够触发Json格式化函数MarshalJSON。
package main import ( "encoding/json" "fmt" ) type Profile struct { Level string Admin bool } // 本质是将Profile指针类型实现Marshaler接口,从而达到自定义json序列化格式的目的。 func (p *Profile) MarshalJSON() ([]byte, error) { if p.Admin { admin := struct { Level string }{ Level: "admin", } return json.Marshal(admin) } control := struct { Level string Admin bool }{ Level: "control", Admin: false, } return json.Marshal(control) } type User struct { Id int Name string Age uint8 Profile *Profile } func main() { u := User{ Id: 1, Age: 23, Name: "qshuai", Profile: &Profile{ Level: "master", Admin: true, }, } b, err := json.Marshal(u) if err != nil { panic(err) } fmt.Println(string(b)) } // -----------------------------slice作为Struct成员的情况---------------------------- package main import ( "encoding/json" "fmt" ) type Profile struct { Level string Admin bool } func (p *Profile) MarshalJSON() ([]byte, error) { if p.Admin { admin := struct { Level string }{ Level: "admin", } return json.Marshal(admin) } control := struct { Level string Admin bool }{ Level: "control", Admin: false, } return json.Marshal(control) } type User struct { Id int Name string Age uint8 Profile []Profile } func main() { u := User{ Id: 1, Age: 23, Name: "qshuai", Profile: []Profile{ { Level: "master", Admin: true, }, }, } b, err := json.Marshal(u) if err != nil { panic(err) } fmt.Println(string(b)) }
-
使用map[string]interface{}接收不确定json数据
data := []byte(` { "address": "16WtgAckGAYLHaxJnRFV5mueF8gaEbKc4W", "received": {"name":"qshuai", "age":23}, "sent": ["abc", 124, "def"] }`) var d map[string]interface{} err := json.NewDecoder(bytes.NewReader(data)).Decode(&d) if err != nil { panic(err) } spew.Dump(d) // output (map[string]interface {}) (len=3) { (string) (len=7) "address": (string) (len=34) "16WtgAckGAYLHaxJnRFV5mueF8gaEbKc4W", (string) (len=8) "received": (map[string]interface {}) (len=2) { (string) (len=4) "name": (string) (len=6) "qshuai", (string) (len=3) "age": (float64) 23 }, (string) (len=4) "sent": ([]interface {}) (len=3 cap=4) { (string) (len=3) "abc", (float64) 124, (string) (len=3) "def" } }
-
不可寻址的情况
常量
map的值且值为值类型
函数返回值并且返回值为值类型
接口断言且断言成值类型成功
函数返回值为数组类型(非数组指针类型),对返回值直接做[m:n]取slice的操作是不合法的
-
赋值简写方式的坑
// 1、同一作用域 := 左侧至少有一个未申明 // 2、同一作用域 := 左侧重复的变量名的变量地址是相同的 package main import "fmt" func main() { a, b := 1, 2 fmt.Printf("%p\n", &a) a, c := 2, 3 fmt.Printf("%p\n", &a) fmt.Println(b, c) }
-
for…range 坑
package main import "fmt" type T struct { ID int } func (t *T) PrintID() { fmt.Println(t.ID) } func F1() { ts := []T{{1}, {2}, {3}} for _, t := range ts { // 每次遍历,将ts中的相应的元素拷贝到临时变量t,同一个迭代的t的内存地址是相同的 defer t.PrintID() // 先取出t的地址,然后压栈 } } func F2() { ts := []*T{&T{1}, &T{2}, &T{3}} for _, t := range ts { defer t.PrintID() } } func main() { fmt.Println("F1()") F1() fmt.Println() fmt.Println("F2()") F2() }
== nil
的判断结果: 如果左侧为接口类型,那么只有接口变量的值和类型均为nil的情况下,== nil
才能成立;如果左侧是普通数据类型,那么只要其变量的值为nil,那么== nil
就会成立。以
Example_
开头的函数会在go test
时自动运行。golang中的引用类型:
slice、map、channel、function、interface、pointer
-
nil指针可以调用自己的方法,但是不能在函数内对指针进行解引用或者访问其成员变量
package main import "fmt" type user struct { ID int Name string } func (u *user) getInfo() { fmt.Println("hello world") // fmt.Println(u.Name) // will panic } func main() { var u *user if u == nil { fmt.Println("u == nil") } u.getInfo() // ok }
-
nil slice除了不能使用索引访问外,其他操作都是可以的
package main import "fmt" func main() { var s []int s[0] = 1 // panic fmt.Println(s[1]) // panic fmt.Println(len(s), cap(s)) // 0 0 for index, item := range s { // nothing fmt.Println(index, item) } s = append(s, 1) fmt.Println(s) // [1] }
-
nil map除了不能写入元素外,其他操作都是允许的(可以把nil map看成只读的map)
package main import "fmt" func main() { var m map[int]string fmt.Println(m == nil) // true fmt.Println(len(m)) // 0 for key, value := range m { // nothing fmt.Println(key, value) } item, ok := m[2] fmt.Println(item, ok) // false m[0] = "qshuai" // panic m1 := map[int]string{} fmt.Println(m1 == nil) // false m1[1] = "qshuai" fmt.Println(m1[1]) // qshuai }
-
nil channel的写入和读取都会造成死锁,而关闭一个nil channel会引起panic
func main() { var c chan int fmt.Println(c == nil) // true <-c // deadlock c <- 3 // deadlock close(c) // panic }
已关闭的channle仍然可以被读取,只不过结果总为对应类型的零值
func main() { cc := make(chan int) go func() { stop: for { select { case v, ok := <-cc: if ok { fmt.Println(v) } else { break stop // will not into for loop } } } }() go func() { var i int for { cc <- i i++ time.Sleep(500 * time.Millisecond) if i > 5 { close(cc) break } } }() time.Sleep(10 * time.Second) }
-
switch中
的fallthrough
用法:fallthrough
将执行下一个case
内的语句, 而不用判断case
的条件是否满足。func main() { i := 3 switch i { default: fallthrough case 1: fmt.Println("ok") case 2: fmt.Println("false") } } // output: ok
-
switch case的高级用法
func isAcceptableKind(kind reflect.Kind) bool { switch kind { case reflect.Chan: fallthrough case reflect.Complex64: fallthrough case reflect.Complex128: fallthrough case reflect.Func: fallthrough case reflect.Ptr: fallthrough case reflect.Interface: return false } return true }
-
slice用法
data[:6:8] 每个数字前都有个冒号, slice内容为data从0到第6位,长度len为6,最大扩充项cap设置为8
-
检查关闭通道
w.quitMu.Lock() // select 中只有一个通道操作,之所有用select是因为,select可以通过default的方式避开 // 堵塞的情况 select { case <-w.quit: w.quitMu.Unlock() return default: }