2012-03-31 105 views
3

在Go中,如果我用指針定義一個函數作爲接收器,它不應該只允許從指針調用函數嗎?爲什麼從該值本身調用該函數並且具有相同的效果是可以的。Go用指針接收器調用函數的語法

例如,在以下程序中:m1.reset()& m2.reset()具有相同的效果。儘管m1是一個值,m2是一個指針。

我有點困惑,因爲有做同樣的事情的兩種方法,我不知道哪一個要遵循。儘管大部分代碼遵循使用指針字段調用函數的約定。我錯過了什麼嗎?

package main 

    import "fmt" 

    type MyStruct struct { 
     X int 
    } 

    func (m *MyStruct) reset() { 
     m.X = 0 
    } 

    func main() { 
     m1 := MyStruct{1} 
     m2 := &MyStruct{1} 

     fmt.Println(m1.X) 
     fmt.Println(m2.X) 

     m1.reset() 
     m2.reset() 

     fmt.Println(m1.X) 
     fmt.Println(m2.X) 
    } 
+0

你也可以添加,難道不應該取消引用接收參數傳遞,'M * MyStruct',在函數'reset()'的定義中訪問它的字段'X',就像'(* m).X = 0'。但這也是方便的簡寫或語法糖。 – 2015-06-04 09:39:43

回答

4

規格says

對應的指針類型* T的方法集是一組與接收器的所有方法* T或T(即,它也包含T的方法集)。

method calls下一條必要的信息表示:

一個方法調用x.m()是有效的,如果(的類型)的方法集x包含m和參數列表可以被分配到的m的參數列表。如果x是可尋址的且& x的方法組包含m,則x.m()(&x).m()的簡寫。

把上述兩個東西放在一起,你會得到你看到的行爲。

+0

另外一個可以添加,「不應該它取消引用接收機參數傳遞,'米* MyStruct',訪問函數的定義內的外地''X' reset'(),如'(*米).X = 0'?「。但是,這也是一個方便的簡寫或語法糖。 – 2015-06-04 09:40:09

9

@jnml提供了完美的文檔規範解釋,但我想添加一個基於您的代碼示例。我認爲你的重點不應該放在「爲什麼有兩種方法可以做同樣的事情」,更多的是關於何時使用一種方法。有一個指針作爲接收者的方法能夠修改接收者的值,而一個有接收者價值的方法則不能。這是因爲這些方法會收到接收機的副本。當你得到一個指針的副本時,你仍然可以修改它的值。當您收到值的副本,改變你的這種方法只作更改複印,永不原文:

package main 

import "fmt" 

type MyStruct struct { 
    X int 
} 

func (m *MyStruct) resetPtr() { 
    m.X = 0 
} 

func (m MyStruct) resetValue() { 
    m.X = 0 
} 

func main() { 
    m1 := MyStruct{1} 
    m2 := &MyStruct{1} 

    fmt.Println("Original Values:", m1.X, m2.X) 

    m1.resetPtr() 
    m2.resetPtr() 

    fmt.Println("After resetPtr():", m1.X, m2.X) 

    m1 = MyStruct{1} 
    m2 = &MyStruct{1} 

    m1.resetValue() 
    m2.resetValue() 

    fmt.Println("After resetValue():", m1.X, m2.X) 
} 

輸出

Original Values: 1 1 
After resetPtr(): 0 0 
After resetValue(): 1 1 

你可以看到你的方式訪問這些變量並不是真正的問題。它更多關於在方法內部可以用它們做什麼,以及它們如何作爲參數傳遞給其他函數或方法(被複制)。

2

一個簡短的解釋是,幕後轉到編譯器自動轉換:

m1.reset() 
m2.reset() 

到:

(&m1).reset() 
m2.reset()