我想知道为什么你不能做:
type Foo struct { A int } type Bar Foo foos := []Foo{Foo{1}, Foo{2}} bars := []Bar(foos) //cannot convert foos (type []Foo) to type []Bar
我发现这将需要运行时在片上执行循环以转换每个元素,这将是非惯用的Go。这很有道理。
然而,这可能不会被刚刚走样编译器解决Bar的Foo,所以在内部它们是相同的,他们使用相同类型的头底下?我猜答案虽然不是我好奇为什么。
Bar
Foo
这个:
[]Bar(foos)
是类型转换。根据规范,转换具有特定的规则:
在以下任何一种情况下,x可以将非恒定值转换为类型T: x是分配给T。 x的类型,并且T具有相同的基础类型。 x的类型和T是未命名的指针类型,它们的指针基类型具有相同的基础类型。 x的类型T均为整数或浮点类型。 x的类型和T都是复杂类型。 x是整数或字节或符文的片段,并且T是字符串类型。 x是一个字符串,T是字节或符文的一部分。
在以下任何一种情况下,x可以将非恒定值转换为类型T:
x
T
这里没有适用。为什么?
因为的基础类型[]Foo与的基础类型不同[]Bar。类型[]Foo值不能分配给类型变量[]Bar,请参见此处的可分配性规则。
[]Foo
[]Bar
的基础类型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:
unsafe
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) }
这:*(*[]Bar)(unsafe.Pointer(&foos))表示取的地址foos,将其转换为unsafe.Pointer(根据规范可以将所有指针转换为unsafe.Pointer),然后将Pointer其转换为*[]Bar(再次根据规范Pointer可以将其转换为任何其他指针类型),然后将该指针取消引用(*运算符),因此结果是[]Bar在输出中可以看到的type值。
*(*[]Bar)(unsafe.Pointer(&foos))
foos
unsafe.Pointer
Pointer
*[]Bar
*
[{1} {2}] []main.Bar
笔记:
引用的软件包文档unsafe:
不安全的软件包包含绕过Go程序的类型安全的操作。 导入不安全的软件包可能是不可移植的,并且不受Go 1兼容性准则的保护。
不安全的软件包包含绕过Go程序的类型安全的操作。
导入不安全的软件包可能是不可移植的,并且不受Go 1兼容性准则的保护。
这是什么意思?这意味着您不应该usafe每次都使用包来简化生活。您仅应在特殊情况下使用它,否则将使您的程序确实非常缓慢和复杂。
usafe
在您的程序中,情况并非如此,因为我提出了一个仅需一点重构(Foos并且Bars是切片)的工作示例。
Foos
Bars
unsafe绕过Go的类型安全。这是什么意思?如果您要更改的类型foos(例如,大幅度地更改foos := "trap!"),您的程序仍会编译并运行,但是很可能会发生运行时恐慌。使用它们usafe会丢失编译器的类型检查。
foos := "trap!"
如果您使用我的其他提案(Foos和Bars),则会在编译时检测到此类更改/错别字。