2017-08-12 71 views
3

我是新手gopher,試圖讓我的頭在指針接收器和接口周圍。接口和指針接收器

type Foo interface { 
    foo() 
} 
type Bar struct {} 
func (b *Bar) foo() {} 

根據上述定義..

--- Allowed --------- 
b := Bar{} 
b.foo() 

--- Not allowed ----- 

var foo Foo = Bar{} 

獲取編譯器錯誤: 不能在分配使用欄文字(類型吧)爲Foo類型: 酒吧沒有實現美孚(foo的方法指針接收器)

我知道編譯器在第一種場景中代表我們執行一些指針轉換和解引用。它爲什麼不在第二種情況下做同樣的事情?

+0

所以你不問如何使它工作,你問__why__它不工作? (btw'var foo Foo =&Bar {}'作品) – RayfenWindspear

回答

2

解釋在於,當處理具體結構本身時,它具有適當的信息來自動處理它。您可以閱讀in the tour here說:

Go automatically handles conversion between values and pointers for method calls.

但是當你正在處理一個interface{}類型,它有什麼實際包含在變量較少的信息。它只知道有一個foo()方法。但是這裏有一個微妙的地方,需要額外的解釋,所以這裏是一個例子。

https://play.golang.org/p/Y0fJcAISw1

type Foo interface { 
    foo() 
} 
type Bar struct {} 
func (b *Bar) foo() {} 

type Baz struct {} 
func (b Baz) foo() {} 

func main() { 
    b := Bar{} 
    b.foo() 

    var v Foo = &Bar{} 
    // v = Bar{} // fails 
    v.foo() 

    v = Baz{} 
    v.foo() 
    v = &Baz{} // works too 
    v.foo() 
} 

注意&Baz{}工作,即使它有一個值接收器,而不是相反。原因是一個*Baz指向一個Baz,它們都存在(指針和值),所以很容易獲得該值。當您嘗試執行v = Bar{}時,該值存在,但指針不存在,並且Go不會自動爲interface{}值創建一個值。

這是下指針和接口詳細標題in this blog post

4

簡短的回答var foo Foo = Bar{}所有解釋是行不通的,因爲存儲在接口的具體值是不可尋址。

加長版

請仔細閱讀https://github.com/golang/go/wiki/MethodSets

It is legal to call a pointer-valued method on anything that is already a pointer or whose address can be taken. It is legal to call a value method on anything which is a value or whose value can be dereferenced.

對於上述解釋,你的代碼

b := Bar{} 
b.foo() 

作品,因爲b尋址。

The concrete value stored in an interface is not addressable. Therefore, when you call a method on an interface, it must either have an identical receiver type or it must be directly discernible from the concrete type: pointer- and value-receiver methods can be called with pointers and values respectively, as you would expect. Value-receiver methods can be called with pointer values because they can be dereferenced first. Pointer-receiver methods cannot be called with values, however, because the value stored inside an interface has no address. When assigning a value to an interface, the compiler ensures that all possible interface methods can actually be called on that value, and thus trying to make an improper assignment will fail on compilation.

根據上述說明存儲在一個接口的具體值是不可尋址,因此該代碼,

var foo Foo = Bar{} 

不會工作,因爲存儲在接口的具體值,在這種情況下Bar{} ,不可尋址。

+1

您的回答比我的使用更多的權威來源。希望你最終接受。乾杯! – RayfenWindspear

1

你一半的問題取決於你的值是否是尋址與否:

For an operand x of type T , the address operation &x generates a pointer of type *T to x . The operand must be addressable, that is, either:

  • a variable,
  • pointer indirection, or
  • slice indexing operation; or
  • a field selector of an addressable struct operand; or
  • an array indexing operation of an addressable array.

As an exception to the addressability requirement, x may also be a (possibly parenthesized) composite literal.

Address operators

Bar{}是複合聲明,因此不能尋址。您可以鍵入&Bar{}來創建類型爲*Bar的對象,但該對象被列爲「可尋址性要求的例外」,強化了Bar{}本身不可尋址的想法。

Bar類型可以調用b.foo()儘管Bar.foo()需要一個指針接收器,用於一個很好的理由的可變b

A method call x.m() is valid if the method set of (the type of) x contains m and the argument list can be assigned to the parameter list of m . If x is addressable and &x 's method set contains m , x.m() is shorthand for (&x).m()

Calls

然而,這並不意味着Bar.foo()處於b的方法集。這是因爲b具有類型BarBar.foo()接收值*Bar類型:

A type may have a method set associated with it. The method set of an interface type is its interface. The method set of any other type T consists of all methods declared with receiver type T . The method set of the corresponding pointer type *T is the set of all methods declared with receiver *T or T (that is, it also contains the method set of T).

— from Method sets

由於b的方法集和Foo接口不同,則不能使用var foo Foo = b,儘管b.foo()由編譯器被轉換爲(&b).foo()。否則,var foo Foo = Bar{}將工作。你可以,但是,請使用以下的自Bar.foo()收到*Bar

var foo Foo = &b 
var foo Foo = &Bar{} 
+0

我並不是貶低你的答案,或者暗示有改變,但這可能超過某人稱自己是新手的地鼠。我愛,我們現在有3個不同深度解釋的答案。 – RayfenWindspear