2015-08-08 25 views
4

我想知道爲什麼你不能這樣做:爲什麼你無法轉換切片類型?

type Foo struct { A int } 
type Bar Foo 

foos := []Foo{Foo{1}, Foo{2}} 
bars := []Bar(foos) 
//cannot convert foos (type []Foo) to type []Bar 

,我發現,這將需要運行時在片進行一個循環的每個元素,這將是不地道的圍棋轉換。這是有道理的。

但是,這可能不會被編譯器只是別名BarFoo解決,所以在內部它們是相同的,他們使用相同類型的頭在下面?我猜測答案是否定,但我很好奇爲什麼。

+0

你實質上是在問如何解決類型系統來優化循環。我認爲['unsafe']包(http://golang.org/pkg/unsafe/)可以讓你做到這一點 – Kos

回答

3

此:

[]Bar(foos) 

是一種類型conversion。轉化根據該規範具有特定的規則:

非恆定值x可以轉換爲鍵入任何的這些情況下T

  • xassignableT
  • x的類型和T具有相同的基礎類型。
  • x的類型和T是未命名的指針類型,它們的指針基類型具有相同的基礎類型。
  • x的類型和T都是整數或浮點類型。
  • x的類型和T都是複雜類型。
  • x是一個整數或一個字節或符文片段,T是一個字符串類型。
  • x是一個字符串,而T是一段字節或符文。

這裏沒有適用。爲什麼?

因爲底層類型[]Foo與底層類型[]Bar不相同。[]Foo類型的值不能分配給[]Bar類型的變量,請參見Assignability rules here

基礎類型的Foo相同的基礎類型的Bar,但相同並不適用於切片,其中元素類型是FooBar

所以下面的工作:

type Foo struct{ A int } 

type Foos []Foo 
type Bars Foos 

func main() { 
    foos := []Foo{Foo{1}, Foo{2}} 
    bars := Bars(foos) 

    fmt.Println(bars) 
} 

輸出(嘗試在Go Playground):

[{1} {2}] 

注意,因爲FooBar實際內存中的表示是相同的(因爲基礎Bar的類型是Foo),在這種情況下,使用包unsafe可以「查看」[]Foo的值作爲值[]Bar

type Foo struct{ A int } 
type Bar Foo 

func main() { 
    foos := []Foo{Foo{1}, Foo{2}} 

    bars := *(*[]Bar)(unsafe.Pointer(&foos)) 

    fmt.Println(bars) 
    fmt.Printf("%T", bars) 
} 

此:*(*[]Bar)(unsafe.Pointer(&foos))裝置,其採取的foos的地址,將其轉換爲unsafe.Pointeraccording to spec所有的指針可以被轉換爲unsafe.Pointer),則此Pointer根據規範Pointer可以轉化爲*[]Bar(再次被轉換爲任何其他指針類型),然後解除引用該指針(*運算符),因此結果是類型爲[]Bar的值,如輸出中所示。

輸出(嘗試在Go Playground):

[{1} {2}] 
[]main.Bar 

注:

引用的unsafe包DOC:

包不安全包含繞式步驟操作Go程序的安全性。

導入不安全的程序包可能不可移植,且不受Go 1兼容性準則的保護。

這是什麼意思?這意味着你不應該每次使用包usafe時,它都會讓你的生活更輕鬆。您應該只在特殊情況下使用它,而不使用它會讓您的程序變得非常緩慢和複雜。

在你的程序中,情況並非如此,因爲我提出了一個只用一點重構(FoosBars爲切片)的工作示例。

unsafe圍繞Go的類型安全採取措施。這是什麼意思?如果你想改變foos的類型(例如foos := "trap!"),你的程序仍然可以編譯和運行,但很可能會發生運行時恐慌。使用usafe你會失去編譯器的類型檢查。

雖然如果您使用我的其他建議(FoosBars),則會在編譯時檢測到此類更改/拼寫錯誤。

+0

謝謝@icza,所以底層表示是相同的,它只是阻止轉換的規範 - 不安全的轉換是我以後的! – jpillora

+0

@jpillora請參閱編輯答案。不要使用'unsafe'只是因爲它更容易。如果可能,請使用適當的設計以避免使用「不安全」。 – icza

+0

不夠公平並且同意 - 我目前正在循環轉換每個元素,並且如果性能受到影響,我將只使用不安全的情況,而這種情況很可能不會發生。也許它可以完全避免我猜?用例是:我正在導入一個'foo'包,並且想在我的包中公開它的類型'foo.Foo',所以我重新輸入它以防止我的用戶需要導入我的包*和*'foo'。 – jpillora

1

正如在 「Why can I type alias functions and use them without casting?

提到圍棋,有沒有這樣的事,作爲一個類型別名
type關鍵字引入新的named types。他們不是別名

如果比較兩個命名的類型,名稱必須爲了他們是可以互換的匹配

這就是spec mentions

類型聲明結合的標識符,類型名稱,與具有與現有類型相同的基礎類型的新類型以及爲現有類型定義的操作也爲新類型定義。
新型號與現有型號不同。

相關問題