当前位置: 首页 > 面试题库 >

为什么无法转换切片类型?

濮阳振
2023-03-14
问题内容

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

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。这很有道理。

然而,这可能不会被刚刚走样编译器解决BarFoo,所以在内部它们是相同的,他们使用相同类型的头底下?我猜答案虽然不是我好奇为什么。


问题答案:

这个:

[]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}]

注意,由于实际内存中的表示FooBar是相同的(由于底层类型BarFoo使用包),在此情况下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(再次根据规范Pointer可以将其转换为任何其他指针类型),然后将该指针取消引用(*运算符),因此结果是[]Bar在输出中可以看到的type值。

输出(在Go Playground上尝试):

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

笔记:

引用的软件包文档unsafe

不安全的软件包包含绕过Go程序的类型安全的操作。

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

这是什么意思?这意味着您不应该usafe每次都使用包来简化生活。您仅应在特殊情况下使用它,否则将使您的程序确实非常缓慢和复杂。

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

unsafe绕过Go的类型安全。这是什么意思?如果您要更改的类型foos(例如,大幅度地更改foos := "trap!"),您的程序仍会编译并运行,但是很可能会发生运行时恐慌。使用它们usafe会丢失编译器的类型检查。

如果您使用我的其他提案(FoosBars),则会在编译时检测到此类更改/错别字。



 类似资料:
  • 问题内容: 我有一个子过程命令,输出一些字符,例如’\ xf1’。我正在尝试将其解码为utf8,但出现错误。 上面抛出: 当我使用’latin-1’时它可以工作,但是utf8也不能工作吗?我的理解是latin1是utf8的子集。 我在这里想念什么吗? 编辑: 问题答案: 您已经将Unicode与UTF-8混淆了。Latin-1是Unicode的子集,但不是UTF-8的子集。 避免像瘟疫一样思考各个

  • 问题内容: 考虑以下: 我一直在寻找背后的逻辑,但是没有运气。值得一提的是,如果将结构更改为类,则效果很好。 总是可以添加一种解决方法,并将fooArray的每个对象映射为强制类型,将它们转换为Any类型,但这不是这里的问题。我正在寻找一种解释,为什么会这样。 有人可以解释一下吗? 问题答案: Swift 3更新 从Swift 3(特别是Xcode 8 beta 6附带的内部版本)开始,集合类型现

  • 问题内容: 我刚开始使用Go,所以这很明显。编译器不允许以下代码:(http://play.golang.org/p/3sTLguUG3l) 错误是: 从规格上看,[] string看起来与[] Card不是相同的基础类型,因此无法进行类型转换。 确实是这样,还是我错过了什么? 如果是这样,为什么会这样呢?假设在一个非宠物示例程序中,我输入了一个字符串切片,是否可以将其“投射”到Card切片中,或

  • 问题内容: 一个激励人的例子: 实施各种调度“策略”,对“作业”列表进行排序。 一种非常简单的策略是首先执行最短的作业(不考虑其权重/优先级)。 嗯,这种策略只不过是对job.length进行排序,因此让我们使用sort包。定义一个自定义类型,并实现sort.Interface … 好了,现在回到我们的简单策略… 嗯… 问题答案: 首先, 即使您使用like ,也看不到任何定义 。 我认为您的意思

  • 问题内容: 目前,我这样做是为了将CGO的double数组转换为float64的一部分: 是否有一种不太麻烦的方式来做相同的事情?我想这也可以看作是Go中不同类型的切片/数组之间进行转换的一种通用方法。 问题答案: 您具有执行此操作的正常和安全的方法: 或者,您可以不方便地作弊并执行以下操作: 如果和是相同的基础类型,我们可以使用指向C.double数组的指针,不安全地将其强制转换为相同大小的fl

  • 我正在尝试创建一个基本的谷歌地图android应用程序。就在我创建项目并获得API密钥之后,我就收到了这个错误。 “错误:不兼容类型:片段无法转换为SupportMapFragment” 行:SupportMapFragment mapFragment=(SupportMapFragment)getSupportFragmentManager().findFragmentByID(r.id.map