再議go語言的value receiver和pointer receiver

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

再議struct的value method和pointer method

以一個例子開頭

package mainimport (    "fmt"    _ "unsafe"    _ "reflect")type MyInterface interface {    foo()}type MyStruct struct {   ii int64}func (m * MyStruct) foo() {   fmt.Println(m.ii);   m.ii ++}func Hello(p MyInterface) {   p.foo();}func main() {   m := MyStruct { 10 }   Hello(m)   fmt.Println(m.ii);}

這段代碼編譯就會出錯

$ go build main.go# command-line-arguments./main.go:29: cannot use m (type MyStruct) as type MyInterface in argument to Hello:        MyStruct does not implement MyInterface (foo method has pointer receiver)

意思是說MyStruct沒有實現MyInterface說聲稱的方法foo,因為foo被聲明成了pointer receiver,而實際需要的是value receiver。

分析原因
golang官方文檔對value method和pointer method有一個解釋:
https://golang.org/doc/effective_go.html#pointers_vs_values

The rule about pointers vs. values for receivers is that value methods can be invoked on pointers and values, but pointer methods can only be invoked on pointers.

This rule arises because pointer methods can modify the receiver; invoking them on a value would cause the method to receive a copy of the value, so any modifications would be discarded. The language therefore disallows this mistake.

解釋一下就是

  1. value method 可以被 pointer和value 對象調用,而
    pointer method 只能被 pointer 對象調用
func (m * MyStruct) foo1();func (m MyStruct) foo2();m := MyStruct { 10 }n := &m//m.foo1(), compiler will fail this line, because foo1() is pointer method, cannot be called from value object m.m.foo2()   // notice, value modification will be discardedn.foo1()   //  notice, value modification will be kept in m n.foo2()   // notice, value modification will be discarded
  1. 原因是:pointer method會修改對象的值,而value method不會,所以如果在一個value對象上調用pointer method,編譯器會對原來的值做一份拷貝(參考函數傳參規範),並在拷貝後的值上執行函數,那麼如果函數有修改原receiver的值,則修改的行為都發生在拷貝的值上,而不會影響原值,這個錯誤很隱蔽不容易被調試發現,因此go決定放棄這個錯誤發生的可能性,直接不支援pointer method被value對象調用。(用心良苦呀,但是我喜歡這樣,就是你連犯錯誤的可能性和機會都不給)

下面一段我就看不明白了,不知道value is addressable是什麼意思。

There is a handy exception, though. When the value is addressable, the language takes care of the common case of invoking a pointer method on a value by inserting the address operator automatically.

回到我們的代碼
因為m是一個值變數,而foo是一個pointer method,正好是前面分析的不支援的情境;那麼如何更改,只要把調用Hello的時候取地址即可

func main() {   m := MyStruct { 10 }   // Hello(m)   Hello(&m)   fmt.Println(m.ii);}

運行後我們得到

$ go build main.go && ./main 1011

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.