經常會有一些朋友問go語言的一些問題和疑惑,其實好多問題在官方文件和stackoverflow裡都有詳細的講解,只要你肯花時間讀一遍官方文件和Effective Go基本上都有找到答案。本文總結一下大家經常問到的一些問題,長期更新。
程式碼都在github上, 地址 https://github.com/lpxxn/gocommonquestions
new 和make 的區別
簡單來說,new(T)用於分配記憶體,返回指向T型別的一個指標,指標的值為T型別的零值
n1 := new(int) fmt.Println(n1) // console the address of n1 fmt.Println(*n1 == 0) // zero value type T struct { I int A string Next *T } n2 := new(T) fmt.Println(n2) fmt.Println(n2.I == 0) fmt.Println(n2.Next == nil) fmt.Println(n2.A == "") n3 := new([]int) fmt.Println(n3) fmt.Println(*n3 == nil)
make(T) 只能用於slice、map和channel, 返回非零值的T。
m1 := make([]int, 1) fmt.Println(m1) m2 := make(map[int]string) m2[0] = "abcde" m3 := make(chan int) m4 := make(chan int, 5)
make 返回的是型別本身,new 返回的是指向型別的指標
相關講解
https://stackoverflow.com/questions/9320862/why-would-i-make-or-new
https://golang.org/doc/effective_go.html#allocation_new
https://golang.org/ref/spec#The_zero_value
是否需要主動關閉channel
除非你的程式需要等待channel關閉後做一些操作,不用主動去關閉channel。當沒有地方在使用這個channel的時候go的垃圾回收系統會自動回收,如果channel裡還有值,但是沒有地方引用了,也會被回收。
下面的小例子就是在等待channel c1和c2關閉後,再做一些事情。
func main() { c1 := make(chan int, 3) go test1(c1) for v := range c1 { fmt.Println(v) } fmt.Println("after close c1 do something") c2 := make(chan bool) go func() { time.AfterFunc(time.Second * 3, func() { close(c2) }) }() _, close := <- c2 if !close { fmt.Println("after c2 closed do something") } fmt.Println("end") } func test1(c chan<- int) { for i := 0; i < 5; i++ { c <- i } close(c) }
簡單測試垃圾回收channel
func createChan() chan int64{ c := make(chan int64, 10000) c <- 1 return c } func main() { RM() for i := 0; i < 10; i++ { c := createChan() c <- 2 RM() runtime.GC() } RM() } func RM(){ var m runtime.MemStats runtime.ReadMemStats(&m) fmt.Println(m.Alloc) }
相關講解:https://stackoverflow.com/questions/8593645/is-it-ok-to-leave-a-channel-open
https://groups.google.com/forum/#!msg/golang-nuts/pZwdYRGxCIk/qpbHxRRPJdUJ
https://groups.google.com/forum/#!topic/golang-nuts/KtIyc5lTRJY
Unbuffered channel和buffered channel 區別
buffered channel
c3 := make(chan bool, 5) // buffered channel
buffered channel 可以持續的傳送資料到channel,直到channel滿為至。不用等待是否有接收channel。
如果channel滿了,會等待讀取channel,當有channel被讀取,就會繼續傳送資料到channel
c3 := make(chan bool, 5) // buffered channel go func() { for i := 0; i < 20; i++ { c3 <- i % 2 == 0 } close(c3) }() for v := range c3 { fmt.Println(v) }
unbuffered channel
下面這兩種宣告是一樣的
c1 := make(chan bool, 0) // unbuffered channel c2 := make(chan bool) // unbuffered channel
unbuffered channel 的接收channel會一直阻塞,直到有值傳給channel, 也可以說傳送channel會一直阻塞,至到有接收channel
c1 := make(chan bool, 0) // unbuffered channel c2 := make(chan bool) // unbuffered channel go func() { c1 <- false time.Sleep(time.Second * 2) c2 <- true }() fmt.Println(<-c1) fmt.Println(<-c2)
相關講解:
https://golang.org/doc/effective_go.html
定義型別和組合型別的區別
定義型別,也可以說是別名和組合型別的區別
有一個Test的結構,NewTest是以Test為型別的一個定義,New2Test和New3Test都是組合型別
type Test struct { N int } func (m *Test) Name() { fmt.Println("abc")} // NewTest does not inherit any functions of Test // can access fields type NewTest Test // New2Test is composite type, it inherit all functions of Test // can access fields type New2Test struct { Test } // if embedded type is pointer you must initialized it type New3Test struct { *Test }
1.定義型別NewTest 相當於一個新的型別,他不能直接呼叫Test的方法,但是可以訪問Test的欄位。如果想呼叫原型別的方法需要做轉換
2.New2Test和New3Test都是組合型別,他倆都可以直接呼叫Test的方法和訪問Test的欄位,他倆的不同之處就是一個是值組合一個是指標組合
3.在例項化New3Test的時候需要手動例項化*Test指標
n := NewTest{} n.N = 1 // n have no method // n.Name() // error v := (*Test)(&n) v.Name() v2 := Test(n) v2.Name() n2 := New2Test{} n2.N = 2 n2.Name() n3 := New3Test{Test: new(Test)} // access filed N will panic if you do not initialized *Test n3.N = 3 n3.Name()
相關的解答:
https://golang.org/ref/spec#Type_declarations
---