go - 为什么不能转换 Slice 类型?

标签 go type-conversion slice type-alias

我想知道为什么你不能这样做:

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

我发现这需要运行时在 slice 上执行循环以转换每个元素,这将是非惯用的 Go。这是有道理的。

但是,这难道不能通过编译器将 Bar 别名为 Foo 来解决吗,所以在内部它们是相同的并且它们在下面使用相同的类型 header ?我猜答案是否定的,尽管我很好奇为什么。

最佳答案

这个:

[]Bar(foos)

是一个类型 conversion .根据规范,转换具有特定规则:

A non-constant value x can be converted to type T in any of these cases:

  • x is assignable to T.
  • x's type and T have identical underlying types.
  • x's type and T are unnamed pointer types and their pointer base types have identical underlying types.
  • x's type and T are both integer or floating point types.
  • x's type and T are both complex types.
  • x is an integer or a slice of bytes or runes and T is a string type.
  • x is a string and T is a slice of bytes or runes.

此处不适用。为什么?

因为 []Foo 的底层类型与 []Bar 的基础类型不同. 和一个 []Foo 类型的值不可分配给 []Bar 类型的变量, 请参阅 Assignability rules here .

Foo 的底层类型与 Bar 的基础类型相同, 但同样不适用于元素类型为 Foo 的 slice 和 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)
}

这个:*(*[]Bar)(unsafe.Pointer(&foos))意味着取 foos 的地址, 将其转换为 unsafe.Pointer ( according to spec 所有指针都可以转换为 unsafe.Pointer ),那么这个 Pointer转换为 *[]Bar (再次根据规范 Pointer 可以转换为任何其他指针类型),然后该指针被取消引用( * 运算符),因此结果是 []Bar 类型的值从输出中可以看出。

输出(在 Go Playground 上尝试):

[{1} {2}]
[]main.Bar

注意事项:

引用 unsafe 的包文档:

Package unsafe contains operations that step around the type safety of Go programs.

Packages that import unsafe may be non-portable and are not protected by the Go 1 compatibility guidelines.

这是什么意思?这意味着你不应该恢复使用包 usafe每次它都会让您的生活更轻松。您应该只在特殊情况下使用它,不使用它会使您的程序变得非常缓慢和复杂。

在您的程序中,情况并非如此,因为我提出了一个仅需少量重构的工作示例(FoosBars 是 slice )。

unsafe绕过 Go 的类型安全。这是什么意思?如果您要更改 foos 的类型(例如非常像 foos := "trap!" ),你的程序仍然可以编译和运行,但很可能会发生运行时 panic 。使用 usafe你失去了编译器的类型检查。

如果您使用我的其他建议(FoosBars),此类更改/打字错误会在编译时检测到。

关于go - 为什么不能转换 Slice 类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31891493/

相关文章:

将 sha 256 哈希转换为 c 中的数字

go - 在 slice 上运行递归函数时发生意外行为

mongodb - 在 golang 中使用 griddfs 从 Mongodb 下载文件

Java 泛型转换不符合预期

json - 在 Go 中使用匿名成员展平编码的 JSON 结构

c++ - 关于 C++ 中常量的隐式转换

python - 关于Python的切片符号

rust - 将结构转换为数组是否合法?

string - 从阅读器读取直到到达一个字符串

go - 读取Go命令行程序中管道输入的长度