将一段字符串转换为一段自定义类型
我对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
从规格,它看起来像[]串是不一样的基本类型为[]卡,所以不能进行类型转换。
确实是这样,或者我错过了什么?
如果是这样的话,为什么这样呢?假设在一个非宠物示例程序中,我输入了一串字符串,是否有任何方法可以将它“投射”到一张卡片中,还是必须创建一个新结构并将数据复制到它中? (我想避免,因为我需要调用的函数将修改切片内容)。
基础类型的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?
正如我提到的,这是一个宠物示例,在我的真实设置中,'[] string'来自我无法控制的外部源。 – 2015-03-13 13:08:19
@FDO是的,我知道,但如果这是一个宠物的例子,在更复杂的例子中,首先创建适当的类型更有理由。看看它,因为在这个例子中,你没有所需的输入切片,只有一些数据可以产生所需的输入切片。 – icza 2015-03-13 13:11:11
我不确定这是什么意思。例如'strings.Split'的输出是一个'[]字符串'。我创建了'Card'和'Hand'类型,因为我需要这些类型的'方法','strings.Split'的输出用作这些类型的输入,看起来像是浪费时间/ cpu /代码将输入复制到新的数据结构中。 为什么'[] string'和'[] Card'具有不同的内存布局? ('[] int32'和'[] byte'的例子很明显,但在我看来,它并不是很明显) – 2015-03-13 13:19:33
从规格来看,[]字符串与[]卡的基本类型不同,因此不能进行类型转换。
没错。您必须通过在每个元素上循环和复制来转换它,在途中将类型从string
转换为Card
。
如果是这样,为什么这样呢?假设在一个非宠物示例程序中,我输入了一串字符串,是否有任何方法可以将它“投射”到一张卡片中,还是必须创建一个新结构并将数据复制到它中? (我想避免,因为我需要调用的函数将修改切片内容)。
因为转换总是明确的,设计者认为转换隐含地涉及副本时,它也应该是明确的。
不过,我不明白为什么需要一个副本(是否有任何方法可以避免它?)。我的理解仍然缺失。 – 2015-03-13 11:51:35
@FDO,这是一个副本,因为一切都是一个值。没有办法做一个变量,并且分配一个值而不需要拷贝某些东西,即使它只是一个指针。 – JimB 2015-03-13 12:55:53
没有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这是拒绝“直到有一个更令人信服的理由”。
这是一个非常有趣的免费评论,我希望我可以不止一次提高其知名度。 – 2018-02-08 06:56:23
通过'类型卡字符串'你引入了一个实际的新类型,而不是一个类型别名。 'Card'是一种新的类型,它可以拥有一套方法和行为。 – 2015-03-13 12:36:40