我对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
从规范来看,它看起来像[]字符串与[]卡的底层类型不同,因此不能进行类型转换。
确实是这样吗,还是我错过了什么?
如果是这样,为什么会这样呢?假设,在一个非宠物示例程序中,我输入一个字符串片段,有没有办法将它“转换”成一片卡片,或者我是否必须创建一个新结构并将数据复制到其中? (我想避免使用,因为我需要调用的函数会修改切片内容。)
答案 0 :(得分:8)
Card
的基础类型可能与string
的基础类型(本身:string
)相同,但基础类型[]Card
不是与基础类型[]string
相同(因此同样适用于Hand
)。
如果T1
与T2
不相同,则无法将T1
切片转换为T2
切片,而不是它们具有什么基础类型的问题。 {1}},你不能。为什么?因为不同元素类型的切片可能具有不同的内存布局(内存中的大小不同)。例如,类型[]byte
的元素各占1个字节。 []int32
的元素各占4个字节。显然,即使所有值都在0..255
范围内,您也无法将其中一个转换为另一个。
但回到根源:如果你需要一片Card
,你为什么要首先创建一片string
?您创建了类型 Card
,因为它不一个string
(或者至少不只是string
)。如果是,并且您需要[]Card
,那么首先创建[]Card
,您的所有问题都会消失:
value := []Card{"a", "b", "c"}
firstHand := NewHand(value)
fmt.Println(firstHand)
请注意,您仍然可以使用无类型常量Card
文字初始化string
切片,因为它可用于初始化任何基础类型为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?
答案 1 :(得分:4)
没有技术原因可以禁止在元素具有相同基础类型(例如[]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
允许程序打败类型系统并读写任意内存。它应该非常小心使用。
2011年有一个关于转化和基础类型的discussion on the mailing list,2016年的proposal to allow conversion between recursively equivalent types被拒绝“直到有更令人信服的理由”。
答案 2 :(得分:1)
从规范来看,它看起来像[]字符串与[]卡的底层类型不同,因此不能进行类型转换。
完全正确。您必须通过循环和复制每个元素来转换它,并在途中将类型从string
转换为Card
。
如果是这样,为什么会这样呢?假设,在一个非宠物示例程序中,我输入了一段字符串,是否有任何方法可以使用#34; cast"它变成了一片卡片,还是我必须创建一个新结构并将数据复制到其中? (我想避免使用,因为我需要调用的函数会修改切片内容)。
因为转换总是明确的,并且设计人员认为当转换隐含地涉及副本时,它也应该是明确的。