为什么你无法转换Slice类型?(Why are you unable convert Slice types?)

系统教程 行业动态 更新时间:2024-06-14 16:54:47
为什么你无法转换Slice类型?(Why are you unable convert Slice types?)

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

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别名来解决这个问题,所以内部它们是相同的并且它们在下面使用相同的类型头? 虽然我很好奇为什么,但我猜测的答案是否定的。

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.

最满意答案

这个:

[]Bar(foos)

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

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

x 可赋予T x的类型和T具有相同的底层类型。 x的类型和T是未命名的指针类型,它们的指针基类型具有相同的基础类型。 x的类型和T都是整数或浮点类型。 x的类型和T都是复杂类型。 x是整数或字节或符文切片, T是字符串类型。 x是一个字符串, T是一个字节或符文的片段。

这里没有适用的。 为什么?

因为[]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的值:

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 (再次根据spec Pointer可以转换为任何其他指针类型),然后这个指针被解引用( *运算符),所以结果是类型[]Bar的值,如可以看到输出。

输出(在Go Playground上试试):

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

笔记:

引用unsafe的包文档:

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

导入不安全的软件包可能是不可移植的,并且不受Go 1兼容性指南的保护。

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

在你的程序中,情况并非如此,因为我提出了一个只有一点重构的工作示例( Foos和Bars是切片)。

围绕Go类型安全的unsafe步骤。 这是什么意思? 如果你要改变foos的类型(例如像foos := "trap!" ),你的程序仍然可以编译和运行,但很可能会发生运行时恐慌。 使用usafe会丢失编译器的类型检查。

如果您使用我的其他提议( Foos和Bars ),则会在编译时检测到此类更改/拼写错误。

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.

更多推荐

本文发布于:2023-04-08 12:36:00,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/dzcp/7c3d41cfc64866d2b9eade24ea7f2aac.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:类型   Slice   unable   types   convert

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!