为什么你无法转换切片类型? [英] Why are you unable convert Slice types?

查看:143
本文介绍了为什么你无法转换切片类型?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  type Foo struct {A int} 
type Bar Foo

foos:= [] Foo {Foo {1},Foo {2}}
bars:= [] Bar(foos)
//不能转换foos(type [] Foo)键入[] Bar

,我发现这需要运行时执行在切片上循环以转换每个元素,这将是非惯用的Go。这是有道理的。然而,编译器只能将 Bar 别名 Foo ,所以在内部它们是相同的,它们在下面使用相同类型的头文件?

解决方案

这样:

$ b我猜测答案是否定的,尽管我很好奇为什么。
$ b

  [] Bar(foos)

是一种转换。根据规范,转化有特定的规则:


非常数值 x 在以下任何情况下都可以转换为 T 类型:


  • x 转让 T

  • x 的类型和 T 具有相同的基础类型。

  • x 的类型和 T 是未命名的指针类型,它们的指针基类型具有相同的基础类型。
  • x 的类型和 T

  • x 的类型和 T 都是复杂类型。
  • x 是一个整数或一段字节或符文, T 是一个字符串类型。
  • x 是一个字符串, T 是一段字节或符文。


这里不适用。为什么?



因为 [] Foo 的基础类型与底层类型不同 [] Bar 类型 [] Foo 的值不能分配给类型变量 [] Bar ,请参阅此处的可分配规则
$ b

Foo 的底层类型与 Bar的底层类型相同,但同样不适用于元素类型为 Foo Bar



因此,下面的工作:

pre $ F $ struct F int}

类型Foos [] Foo
类型酒吧Foos

func main(){
foos:= [] Foo {Foo {1} ,Foo {2}}
酒吧:=酒吧(foos)

fmt.Println(酒吧)
}

输出(在 Go Playground ):

  [{1} {2}] 

请注意,由于 Foo Bar 的实际内存表示相同(因为 Bar 的基础类型是 Foo ),在这种情况下使用包 不安全 ,您可以查看 [] 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 根据规格,所有指针都可以转换为 unsafe.Pointer ),那么这个指针转换为 * [] Bar (同样根据规格指针可以转换为任何其他指针类型),然后解析这个指针( * operator),所以结果是一个类型为 []的值Bar 可以在输出中看到。



输出(在 Go Playground ):

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

注意:
$ b 引用不安全的包文件:


包不安全包含围绕Go程序类型安全的操作。



导入不安全的程序包可能不可移植,且不受保护Go 1兼容性指南es。


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



在你的程序中,情况并非如此,因为我提出了一个工作( Foos Bars 为切片)。



不安全围绕Go的类型安全采取措施。这是什么意思?如果你想改变 foos 的类型(例如像 foos:=trap!),你的程序会仍然编译并运行,但很可能会发生运行时恐慌。使用 usafe 你会失去编译器的类型检查。



如果你使用我的其他建议( Foos Bars ),这些更改/拼写错误会在编译时检测到。


I was wondering why you can't do:

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

and I found out that this would require the runtime to perform a loop over the slice to convert each of the elements, which would be non-idiomatic Go. This makes sense.

However, could this not be solved by the compiler just aliasing Bar as Foo, so internally they're the same and they use the same type header underneath? I'm guessing the answer is no though I'm curious as to why.

解决方案

This:

[]Bar(foos)

is a type conversion. Conversions have specific rules according to the spec:

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.

None applies here. Why?

Because the underlying type of []Foo is not the same as the underlying type of []Bar. And a value of type []Foo is not assignable to a variable of type []Bar, see Assignability rules here.

The underlying type of Foo is the same as the underlying type of Bar, but the same does not apply to slices where the element type is Foo and Bar.

So the following works:

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)
}

Output (try it on the Go Playground):

[{1} {2}]

Note that since the actual memory representation of Foo and Bar is the same (because the underlying type of Bar is Foo), in this case using the package unsafe you can "view" a value of []Foo as a value of []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)) means that take the address of foos, convert it to unsafe.Pointer (according to spec all pointers can be converted to unsafe.Pointer), then this Pointer is converted to *[]Bar (again according to the spec Pointer can be converted to any other pointer type), and then this pointer is dereferenced (* operator), so the result is a value of type []Bar as can be seen in the output.

Output (try it on the Go Playground):

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

Notes:

Quoting the package doc of 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.

What does this mean? It means that you shouldn't revert to using package usafe every time it makes your life easier. You should only use it in exceptional cases, when not using it would make your program really slow and complicated.

In your program this is not the case as I proposed a working example with just a little refactoring (Foos and Bars being slices).

unsafe steps around the type safety of Go. What does this mean? If you would change the type of foos (e.g. drastically like foos := "trap!"), your program would still compile and run, but most likely runtime panic would occur. Using usafe you lose type checks of the compiler.

While if you use my other proposal (Foos and Bars), such changes/typos are detected at compile time.

这篇关于为什么你无法转换切片类型?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆