将一片字符串转换为自定义类型的片

时间:2015-03-13 11:42:37

标签: go type-conversion slice

我对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

从规范来看,它看起来像[]字符串与[]卡的底层类型不同,因此不能进行类型转换。

确实是这样吗,还是我错过了什么?

如果是这样,为什么会这样呢?假设,在一个非宠物示例程序中,我输入一个字符串片段,有没有办法将它“转换”成一片卡片,或者我是否必须创建一个新结构并将数据复制到其中? (我想避免使用,因为我需要调用的函数会修改切片内容。)

3 个答案:

答案 0 :(得分:8)

Card的基础类型可能与string的基础类型(本身:string)相同,但基础类型[]Card不是与基础类型[]string相同(因此同样适用于Hand)。

如果T1T2不相同,则无法将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"它变成了一片卡片,还是我必须创建一个新结构并将数据复制到其中? (我想避免使用,因为我需要调用的函数会修改切片内容)。

因为转换总是明确的,并且设计人员认为当转换隐含地涉及副本时,它也应该是明确的。