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

Conversion of a slice of string into a slice of custom type

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

从规范来看,[]string 与[]Card 的底层类型不同,因此无法进行类型转换。

确实如此,还是我漏掉了什么?

如果是这样,为什么会这样?假设,在一个非 pet 示例程序中,我输入了一段字符串,有没有办法将它 "cast" 放入一张卡片中,或者我是否必须创建一个新结构并复制数据进去? (我想避免这种情况,因为我需要调用的函数会修改切片内容)。

From the specs, it looks like []string is not the same underlying type as []Card, so the type conversion cannot occur.

完全正确。您必须通过循环和复制每个元素来转换它,在途中将类型从 string 转换为 Card

If it is the case, why is it so? Assuming, in a non-pet-example program, I have as input a slice of string, is there any way to "cast" it into a slice of Card, or do I have to create a new structure and copy the data into it? (Which I'd like to avoid since the functions I'll need to call will modify the slice content).

因为转换总是显式的,并且设计者认为当转换隐式涉及副本时,它也应该显式。

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

您不能将 T1 的切片转换为 T2 的切片,如果 T1 与 [=20] 不相同,这与它们的底层类型无关=],你就是做不到。为什么?因为不同元素类型的切片可能有不同的内存布局(内存中的大小不同)。例如 []byte 类型的元素每个占用 1 个字节。 []int32的元素各占4个字节。显然,即使所有值都在 0..255.

范围内,您也不能只将一个转换为另一个

但回到根源:如果您需要一片 Cards,为什么首先要创建一片 strings?您创建了 type Card 因为它 not a string (或者至少不仅仅是 string).如果是这样,并且您需要 []Card,那么首先创建 []Card,您所有的问题都会消失:

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

请注意,您仍然可以使用 untyped 常量 string literals 初始化 Card 的切片因为它可以用来初始化任何底层类型为 string 的类型。如果你想涉及 string 类型的常量或类型 string 的非常量表达式,你需要显式转换,如下例所示:

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

如果您有一个 []string,您需要从中手动构建一个 []Card。没有 "easier" 方法。您可以创建一个辅助 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 {}

没有技术原因禁止元素具有相同基础类型(例如 []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 allows a program to defeat the type system and read and write arbitrary memory. It should be used with extreme care.

2016 年有一个 discussion on the mailing list about conversions and underlying types in 2011, and a proposal to allow conversion between recursively equivalent types 被拒绝 "until there is a more compelling reason"。