如何在Go中将切片转换为别名切片?

6

我将我的Int类型定义为int

我想将一个Int切片转换为int切片,但是出现了编译错误:

无法将c(类型[]Int)转换为类型[]int

我该如何修复这个问题?

package main

import (
    "fmt"
)

type Int int

func main() {
    var c = []Int{}
    var x = []int( c )
    fmt.Println(len(x))
}

你所做的不是别名。你不需要别名。忘掉那个术语。参加Go之旅,它会解释如何转换具有相同基础类型的类型。 - Volker
4个回答

12

你的 Int 类型并不是 int 的别名,它是一种具有 int 作为底层类型的新类型。这种类型的转换在语言规范中不受支持/允许。更具体地说,不允许将元素类型不同的切片类型转换为另一个切片类型。

安全的方式

如果你只需要一个 []int []Int 的"视图",安全的"转换"方式是创建一个具有 []int 类型但数据与 []Int 相同的副本,并使用 for range 循环将每个单独的元素从 Int 转换为 int 类型:

var c = []Int{1, 2}

x := make([]int, len(c))
for i, v := range c {
    x[i] = int(v)
}
fmt.Println(x)

输出结果(在Go Playground上试一下):

[1 2]

不安全的方式

还有一种“不安全”的方式:

var c = []Int{1, 2}

var x []int = *(*[]int)(unsafe.Pointer(&c))
fmt.Println(x)

输出相同。在Go Playground上尝试这个。

这里发生的是,c的地址(即&c)被转换为unsafe.Pointer(所有指针都可以转换为它),然后将其转换为*[]intunsafe.Pointer可以转换为任何指针类型),然后解引用此指针,得到类型为[]int的值。在这种情况下,它是安全的,因为[]Int[]int的内存布局是相同的(因为Int具有int作为其基础类型),但一般来说,应尽可能避免使用unsafe包。

如果Int是一个“真正的”别名

请注意,如果Intint的“真正的”别名,甚至不需要进行转换:

var c = []Int{1, 2}

var x []int = c
fmt.Println(x)

输出结果与上面相同(在Go Playground上尝试)。这个方法有效的原因是,使用[]Int[]int等同,它们是相同的类型,因此您甚至不需要进行转换。

使用切片类型

还要注意,如果您使用[]int作为其基础类型创建一个新类型,则可以使用类型转换:

type IntSlice = []int

func main() {
    var c = IntSlice{1, 2}

    var x []int = []int(c)
    fmt.Println(x)
}

输出结果再次相同。在Go Playground上尝试一下。


有给我点踩的人可以评论一下吗? - icza
这个答案对我来说看起来不错。是的,downvoter应该表达他的观点,我会upvote它来弥补 :) 附言:我注意到你后来添加了一部分内容,恰好我也举例说明了。 - Ravi R
“unsafe”方案本身并不不安全。 - segfault
@segfault 目前版本没有任何“不安全”的地方。Go的向后兼容性保证并不保证使用包 unsafe 的应用程序将在未来版本和所有平台上继续工作。这种不安全的解决方案在未来的版本中破坏的可能性很小,但不能保证。有保证的是,在将来的版本中,安全的方式也将在所有平台上继续工作。 - icza
如果过去的设计决策是任何指示,我不会把它放在他们身上。 - segfault

0

其实,一个切片只是简单地指向了一个指定类型的底层数组(在这种情况下,类型是不同的,Intint)。所以,除非你的底层类型是相同的,否则转换是行不通的。但是,为了说明这一点,像这样做就可以:

package main

import (
    "fmt"
)

type Int int
type IntSl []int

func main() {
    var c = IntSl{2, 3, 4}
    var x []int
    x = []int(c)
    var a Int
    var b int
    a = 1
    b = int(a)
    fmt.Println(len(x), a, b, c)
}

实验室: https://play.golang.org/p/ROOX1XoXg1j

正如 @icza 指出的那样,有一种不安全的方法,当然你也可以遍历每个元素进行转换,但这可能是很昂贵的。


0
问题在于你没有将 Int 创建为别名,而是这样做:

type Int int

这将创建一个无法与 int 互操作的类型。
创建 Int 别名的正确方法是:

type Int = int

通过这个更改,你的程序就可以正常运行了。

1
我不知道为什么会被踩,@johlo的回答真的展示了如何在Go(Go 1.9及更高版本)中创建别名。没有其他人提到Go中真正别名的存在。请参见此处:https://golang.org/doc/go1.9#language - mgagnon

0
从技术角度来看,type Int int 并没有定义一个别名,而是定义了一个全新的类型。尽管 Intint 现在具有相同的基础类型并且可以相互转换,但这并不适用于切片。关于允许的转换更多信息请参阅规范

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接