2015-03-13 75 views
4

我對Go很新,所以這可能很明顯。編譯器不允許以下代碼: (http://play.golang.org/p/3sTLguUG3l將一段字符串轉換爲一段自定義類型

package main 

import "fmt" 

type Card string 
type Hand []Card 

func NewHand(cards []Card) Hand { 
    hand := Hand(cards) 
    return hand 
} 

func main() { 
    value := []string{"a", "b", "c"} 
    firstHand := NewHand(value) 
    fmt.Println(firstHand) 
} 

錯誤是: /tmp/sandbox089372356/main.go:15: cannot use value (type []string) as type []Card in argument to NewHand

從規格,它看起來像[]串是不一樣的基本類型爲[]卡,所以不能進行類型轉換。

確實是這樣,或者我錯過了什麼?

如果是這樣的話,爲什麼這樣呢?假設在一個非寵物示例程序中,我輸入了一串字符串,是否有任何方法可以將它「投射」到一張卡片中,還是必須創建一個新結構並將數據複製到它中? (我想避免,因爲我需要調用的函數將修改切片內容)。

+2

通過'類型卡字符串'你引入了一個實際的新類型,而不是一個類型別名。 'Card'是一種新的類型,它可以擁有一套方法和行爲。 – 2015-03-13 12:36:40

回答

7

基礎類型的Card可能是相同的基礎類型的string(其本身是:string),但基礎類型的[]Card是不一樣的基礎類型的[]string(因此同樣適用於Hand)。

不能的T1片轉換成的T2片,這不是他們有什麼基礎類型的問題,如果T1是不相同的T2,你就不能。爲什麼?由於不同元素類型的切片可能具有不同的內存佈局(內存中的大小不同)。例如,類型[]byte的元素每個佔用1個字節。 []int32的元素各佔用4個字節。顯然,即使所有值都在0..255範圍內,也不能將其中一個轉換爲另一個。

但是回到根源:如果您需要一片Card s,爲什麼首先要創建一片string?您創建了類型Card因爲它是而不是 a string(或至少不只是string)。如果是這樣,你需要[]Card,然後在第一時間創建[]Card和你所有的問題消失:

value := []Card{"a", "b", "c"} 
firstHand := NewHand(value) 
fmt.Println(firstHand) 

請注意,您仍然可以與非類型化不斷string文字因爲初始化的Card片它可以用來初始化任何類型的基礎類型爲string。如果你想涉及類型string常量或string類型的非常量表達式,你需要顯式轉換,如下面的例子:

s := "ddd" 
value := []Card{"a", "b", "c", Card(s)} 

如果你有[]string,你需要從它手工創建一個[]Card 。沒有「更容易」的方式。您可以創建幫助程序toCards()函數,以便您可以在需要它的任何地方使用它。

func toCards(s []string) []Card { 
    c := make([]Card, len(s)) 
    for i, v := range s { 
     c[i] = Card(v) 
    } 
    return c 
} 

背景和推理的一些鏈接:

Go Language Specification: Conversions

why []string can not be converted to []interface{} in golang

Cannot convert []string to []interface {}

What about memory layout means that []T cannot be converted to []interface in Go?

+0

正如我提到的,這是一個寵物示例,在我的真實設置中,'[] string'來自我無法控制的外部源。 – 2015-03-13 13:08:19

+0

@FDO是的,我知道,但如果這是一個寵物的例子,在更復雜的例子中,首先創建適當的類型更有理由。看看它,因爲在這個例子中,你沒有所需的輸入切片,只有一些數據可以產生所需的輸入切片。 – icza 2015-03-13 13:11:11

+1

我不確定這是什麼意思。例如'strings.Split'的輸出是一個'[]字符串'。我創建了'Card'和'Hand'類型,因爲我需要這些類型的'方法','strings.Split'的輸出用作這些類型的輸入,看起來像是浪費時間/ cpu /代碼將輸入複製到新的數據結構中。 爲什麼'[] string'和'[] Card'具有不同的內存佈局? ('[] int32'和'[] byte'的例子很明顯,但在我看來,它並不是很明顯) – 2015-03-13 13:19:33

1

從規格來看,[]字符串與[]卡的基本類型不同,因此不能進行類型轉換。

沒錯。您必須通過在每個元素上循環和複製來轉換它,在途中將類型從string轉換爲Card

如果是這樣,爲什麼這樣呢?假設在一個非寵物示例程序中,我輸入了一串字符串,是否有任何方法可以將它「投射」到一張卡片中,還是必須創建一個新結構並將數據複製到它中? (我想避免,因爲我需要調用的函數將修改切片內容)。

因爲轉換總是明確的,設計者認爲轉換隱含地涉及副本時,它也應該是明確的。

+1

不過,我不明白爲什麼需要一個副本(是否有任何方法可以避免它?)。我的理解仍然缺失。 – 2015-03-13 11:51:35

+0

@FDO,這是一個副本,因爲一切都是一個值。沒有辦法做一個變量,並且分配一個值而不需要拷貝某些東西,即使它只是一個指針。 – JimB 2015-03-13 12:55:53

2

沒有TECHNIC其原因是爲什麼禁止元素具有相同基礎類型的切片(如[]string[]Card)之間的轉換。這是一個規範決定,有助於避免偶然具有相同結構的無關類型之間的意外轉換。

安全的解決方案是複製切片。然而,有可能使用unsafe包直接轉換(無複製):

value := []string{"a", "b", "c"} 
// convert &value (type *[]string) to *[]Card via unsafe.Pointer, then deref 
cards := *(*[]Card)(unsafe.Pointer(&value)) 
firstHand := NewHand(cards) 

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

從包文檔

強制性警告:

unsafe.Pointer允許程序打敗類型系統和讀寫任意內存。應該非常小心地使用它。

有一個discussion on the mailing list有關轉換和基本類型在2011年,並於2016年proposal to allow conversion between recursively equivalent types這是拒絕「直到有一個更令人信服的理由」。

+0

這是一個非常有趣的免費評論,我希望我可以不止一次提高其知名度。 – 2018-02-08 06:56:23

相關問題