这个:
[]Bar(foos)
是一个类型conversion。根据规范,转换有特定的规则:
在以下任何一种情况下,非常量值 x 都可以转换为类型 T:
-
x 是 assignable 到 T。
-
x 的类型和T 具有相同的底层类型。
-
x 的类型和 T 是未命名的指针类型,它们的指针基类型具有相同的基础类型。
-
x 的类型和T 都是整数或浮点类型。
-
x 的类型和T 都是复杂类型。
-
x 是整数或字节切片或符文,T 是字符串类型。
-
x 是一个字符串,T 是一个字节或符文片段。
此处不适用。为什么?
因为[]Foo 的基础类型与[]Bar 的基础类型不同。 而且[]Foo 类型的值不能分配给[]Bar 类型的变量,见Assignability rules here。
Foo 的底层类型与 Bar 的底层类型相同,但同样不适用于元素类型为 Foo 和 Bar 的切片。
所以以下工作:
type Foo struct{ A int }
type Foos []Foo
type Bars Foos
func main() {
foos := []Foo{Foo{1}, Foo{2}}
bars := Bars(foos)
fmt.Println(bars)
}
输出(在Go Playground 上试试):
[{1} {2}]
注意,由于Foo和Bar的实际内存表示是相同的(因为Bar的底层类型是Foo),在这种情况下使用包unsafe你可以“查看” []Foo 的值作为[]Bar 的值:
type Foo struct{ A int }
type Bar Foo
func main() {
foos := []Foo{Foo{1}, Foo{2}}
bars := *(*[]Bar)(unsafe.Pointer(&foos))
fmt.Println(bars)
fmt.Printf("%T", bars)
}
this:*(*[]Bar)(unsafe.Pointer(&foos))表示取foos的地址,转换成unsafe.Pointer(according to spec所有指针都可以转换成unsafe.Pointer),那么这个Pointer就转换成*[]Bar (再次根据规范 Pointer 可以转换为任何其他指针类型),然后取消引用该指针(* 运算符),因此结果是类型为 []Bar 的值,如输出所示.
输出(在Go Playground 上试试):
[{1} {2}]
[]main.Bar
注意事项:
引用unsafe的包文档:
不安全的包包含绕过 Go 程序类型安全的操作。
导入 unsafe 的包可能是不可移植的,并且不受 Go 1 兼容性指南的保护。
这是什么意思?这意味着您不应该每次都使用 usafe 包让您的生活更轻松。你应该只在特殊情况下使用它,如果不使用它会使你的程序变得非常缓慢和复杂。
在您的程序中,情况并非如此,因为我提出了一个工作示例,只需进行一点重构(Foos 和 Bars 是切片)。
unsafe 绕过 Go 的类型安全。这是什么意思?如果您更改foos 的类型(例如,彻底像foos := "trap!"),您的程序仍然可以编译和运行,但很可能会发生运行时恐慌。使用usafe 会丢失编译器的类型检查。
如果您使用我的其他建议(Foos 和 Bars),则会在编译时检测到此类更改/错别字。