go語言的一些吐槽

sniperHW發表於2019-02-16

struct的方法,如果receiver非指標,則呼叫這個方法無法改變物件狀態,因為傳遞給方法的物件是原物件的一個拷貝,所有的改變都被作用在這個拷貝上而非原物件上.

type st struct{
    val uint32
}

func (this st) Show(){
    fmt.Printf("Show:%d
",this.val)
}

func (this st) Increase(){
    this.val += 1
    fmt.Printf("Increase:%d
",this.val)
}

func main(){
    b := st{val:10}
    b.Increase()
    b.Show()
}

輸出:

Increase:11
Show:10

對於習慣了C++的程式設計師,總會認為呼叫一個物件的非const方法是可以改變那個物件的內部狀態.但是這種思維如果延續到go中將會導致很難查詢的bug.

到底是物件實現了介面還是指向物件的指標實現了介面

先來看以下程式碼:

package main

import "fmt"

type intf interface{
    Show()
}

type st struct{
    val uint32
}

func (this *st) Show(){
    this.val += 1
    fmt.Printf("%d
",this.val)
}

func main(){
    var a intf
    b := st{val:10}
    a = b
    a.Show()
}

直觀上我們認為st實現了intf介面,所以可以用b對a賦值,而實際上執行這段程式碼將會報錯:

# command-line-arguments
test/test.go:21: cannot use b (type st) as type intf in assignment:
        st does not implement intf (Show method has pointer receiver)

這段提示說st沒有實現intf介面,因為Show方法的receiver是一個指標.對程式碼稍作修改:

func main(){
    var a intf
    b := &st{val:10}
    a = b
    a.Show()
}

這次程式碼可以正確執行了,此時b應該是一個指向st的指標.這是說*st實現了intf介面?

現在我再把Show的定義改一下,main不變:

func (this st) Show(){
    this.val += 1
    fmt.Printf("%d
",this.val)
}

程式碼還是可以正確執行,同時如果把main恢復成下面這樣:

func main(){
    var a intf
    b := st{val:10}
    a = b
    a.Show()
}

程式碼也是正確的.

也就是說,實現介面的方法時receiver是指標那麼只能用實現型別的指標對介面賦值,否則既能用實現型別的值也能用實現型別的指標對介面賦值.

最後再來看個例子,在一段網路程式碼中,我想將net.Conn轉換成net.TCPConn,如下程式碼報錯:

tcpconn := this.Conn.(net.TCPConn)

# kendynet-go/socket
./tcpsession.go:156: impossible type assertion:
        net.TCPConn does not implement net.Conn (Close method has pointer receiver)
Makefile:2: recipe for target `all` failed

正確的做法是:

tcpconn := this.Conn.(*net.TCPConn)

這是否證明了,是*net.TCPConn而不是net.TCPConn實現了net.Conn介面,

相關文章