2017-06-17 35 views
0

我想了解Go的類型轉換規則。假設我們有以下接口:爲什麼我無法在Go中替換另一種類型的切片?

type woofer interface { 
    woof() 
} 

type runner interface { 
    run() 
} 

type woofRunner interface { 
    woofer 
    runner 
} 

,並滿足我們有一個dog類型的接口:

type dog struct{} 

func (*dog) run() {} 
func (*dog) woof() {} 

這兩個功能使用的接口:

func allWoof(ws []woofer) {} 

func oneWoof(w woofer) {} 

要使用這些方法我可以寫下:

dogs := make([]woofRunner, 10) 
oneWoof(dogs[0]) 
allWoof(dogs) 

第一個功能oneWoof()按預期工作;一個*dog實現所有oneWoof需求,這是一個woof函數。

不過,對於第二個功能allWoof,圍棋將無法編譯嘗試調用,報告如下:

不能用狗(型[] woofRunner)類型[]低音單元在爭論allWoof

使用類型轉換也是不可能的;寫[]woofer(dogs)也將失敗:

不能將狗(型[] woofRunner)鍵入[]低音單元

[]woofRunner每一個成員都擁有所有必要的功能,以滿足[]woofer,所以這是爲什麼禁止轉換?

(我不知道這是否是相同的情況下,在圍棋FAQ並在堆棧溢出,使人們問轉換類型Tinterface{}各種問題解釋切片/陣列中的每個指針指向。我知道一個解決方案是在循環和並轉換:即直接轉換爲另一種類型使用這些指針應爲同樣的原因,經過dog[0]爲「oneWoof`是可能的)

注1可能項目逐一。我的問題是爲什麼這是必要的,是否有更好的解決方案。

注2:就Assignability規則:

甲值x可分配給類型的T A變量[時] T是一個接口類型以及x實現T.

難道我們不能說切片/數組的類型是否可以分配給另一種類型,那麼這些類型的數組也可以賦值?

+1

https://golang.org/ref/spec#Assignability因此,賦值不符合語言標準可分配性規則。 – zerkms

+0

原因:因爲這就是類型轉換的工作原理。不,沒有更好的解決方案。 – Flimzy

+2

請參閱https://golang.org/doc/faq#convert_slice_of_interface。經驗法則:檢查有效圍棋,常見問題解答,圍棋之旅和語言規範。答案就在那裏。 – Volker

回答

2

除了去拒絕切片轉換沿着這裏其他的答案解決這些差異的關係,它想通過是非常有用的,爲什麼去拒絕這樣做,即使在內存中表示將兩者之間的相同類型。

在你的例子中,提供一片woofRunners s作爲[]woofer類型的參數是要求片的元素類型爲covariant treatment。當閱讀從切片,確實,因爲woofRunnerwoofer,你知道每一個存在於[]woofRunner元素將滿足讀者尋找[]woofer

但是,在Go中,切片是引用類型。在將切片作爲參數傳遞給函數時,將複製切片,但在調用的函數體中使用的副本繼續引用相同的後備陣列(在超出其容量之前不需要重新分配)。數組的可變視圖(更一般地說,將項目插入到集合中)需要處理元素類型逆變換。也就是說,當要求功能參數意圖爲插入到覆蓋woofRunner類型的元素時,可以提供[]woofer

的問題是功能是否苛刻的

  • 從中讀取片參數(讀woofer S,A []woofRunner只是作爲[]woofer爲好),
  • 寫入它(爲寫作woofRunner s,一個[]woofer就像[]woofRunner),
  • 或兩者(兩者都不是另一個可接受的替代品)。

考慮一下,如果去沒有接受協變時尚切片中的參數會發生什麼,有人走過來,改變allWoof如下:

// Another type satisfying `woofRunner`: 
type wolf struct{} 
func (*wolf) run() {} 
func (*wolf) woof() {} 

func allWoof(ws []woofer) { 
    if len(ws) > 0 { 
    ws[0] = &wolf{} 
    } 
} 

dogs := []*dog{&dog{}, &dog{}} 
allWoof(dogs) // Doesn't compile, but what if it did? 

即使轉到願意治療[]*dog[]woofer,我們會在*dog這裏列出*wolf。一些語言通過對試圖插入或覆蓋數組的運行時類型檢查來防禦這樣的事故,但由於Go甚至使我們無法實現這一目標,所以它不需要這些額外的檢查。

+0

精彩的回答。謝謝! –

0

您必須轉換的接口組合在一個循環:

var woofers []woofer 
for _, w := range dogs { 
    woofers = append(woofers, w) 
} 
+0

謝謝,但請參閱我的編輯,我在其中澄清我問的問題的類型 –

2

Go reference

兩片類型是相同的,如果它們具有相同的元素類型。

兩種接口類型是相同的,如果他們有相同的一組具有相同名稱和相同的功能類型(...)方法。

所以woofRunner不等同於woofer,這會導致我們[]woofRunner不等同於[]woofer

相關問題