Golang 结构体类型转换

Golang struct type conversion

提问人:richbai90 提问时间:6/22/2022 最后编辑:richbai90 更新时间:7/3/2022 访问量:1089

问:

我试图弄清楚go如何处理结构之间的类型转换。我所读到的所有内容都告诉我,具有相同基础类型的类型被认为是兼容的,并且类型转换是隐式发生的。如果是这样的话,为什么下面的代码不起作用?和实现接口,并且它们都添加了字符串类型的属性。然而,当我将类型的结构传递给期望类型的结构时,我得到一个类型不匹配的编译错误。FooBarFooIxBarAcceptBarOrFooFoo

Go 游乐场

package main

import (
    "play.ground/bar"
    "play.ground/foo"
)

func main() {
    AcceptBarOrFoo(bar.Bar{})
}

func AcceptBarOrFoo(foo.Foo) interface{} {
    return nil
}

// -- iface/iface.go --

package iface

type FooI interface {
    a() string
    b(int) int
}
// -- foo/foo.go --
package foo

import (
    "play.ground/iface"
)

type Foo struct {
    iface.FooI
    x string
}
// -- bar/bar.go --
package bar

import (
    "play.ground/iface"
)

type Bar struct {
    iface.FooI
    x string
}
Go Struct 接口 转换

评论

2赞 Hymns For Disco 6/22/2022
仅当直接分配给接口值时,接口才是多态的。它们没有在语言中赋予任何更广泛的多态性概念。 是结构类型。foo.Foo
1赞 Volker 6/22/2022
Go 中没有更高阶的类型。只需查阅规范,它解释了类型转换。

答:

1赞 user229044 6/22/2022 #1

您永远不能将一种混凝土类型转换为另一种混凝土类型。它们不一样。在 Go 中无法定义这种类型的自动转换。充其量,您可以定义一个函数,该函数接受 并构建并返回一个新的,其字段设置为与输入相同的值。BarFooBar

我所读到的所有内容都告诉我,如果基础类型相同,则认为高阶类型是兼容的,并且类型转换是隐式发生的

目前还不清楚你的消息来源是什么,但没有任何内容会说明或暗示这一点,这根本不是真的。Go 对任何内容进行隐式转换。这是 Go 的一个大而响亮的宣传功能。给定 和 ,您永远不能将 类型的对象赋给 类型的变量。type Foo struct { a int }type Bar struct { a int }BarFoo

当类型满足接口要求时,可以从任一具体类型转换为接口类型。您的方法应该接受接口类型(两者兼而有之),而不是具体类型。假设接口只定义方法(而不是成员),并且给定两者兼而有或没有任何方法,则接口将是空接口。传入的值没有任何作用,除了稍后将其转换回具体类型以访问其成员之外,但这并不是接口的真正用途。AcceptBarOrFooFooBarFooBarinterface{}

评论

0赞 richbai90 6/22/2022
我发现许多类似的文章都与这个答案相同:stackoverflow.com/questions/24613271/...。我现在明白了以前让我感到困惑的细微差别。作为该语言的新手,我发现该规范很有帮助,但很难准确解释大部分类型部分的含义。
3赞 5 revsuser12258482 #2

Foo's 与 Bar's 不同,因为非导出标识符在包边界之间永远不会相等。通过导出 foo 中的字段进行修复。Foo 和酒吧。酒吧:xx

type Foo struct {
    iface.FooI
    X string // <-- start with capital letter to export
}

要使用 foo.Foo 或酒吧。Bar 作为参数值,一个 foo。Foo 和酒吧。Bar 必须可分配给参数的类型。使用 foo 是行不通的。Foo 作为参数类型,因为命名类型不能相互赋值。但是,当命名类型共享相同的基础类型时,命名类型可分配给未命名类型。将参数声明为未命名类型:

func AcceptBarOrFoo(struct {
    iface.FooI
    X string
}) interface{} {
    return nil
}

通过这些更改,将编译以下代码:

AcceptBarOrFoo(bar.Bar{})
AcceptBarOrFoo(foo.Foo{})

在 Go Playground 上运行示例

另一种选择是使用转换为通用类型。在以下代码中,foo.Foo 是常见的类型和条形图。Bar 转换为 foo。傅。

func Accept(foo.Foo) interface{} {
    return nil
}

... 

Accept(foo.Foo{})
Accept(foo.Foo(bar.Bar{}))

在 Go Playground 上运行该示例

注意:foo。Foo 和酒吧。栏必须具有相同的字段才能使上述工作起作用(导出字段名称,字段顺序相同,字段具有相同的类型)。


关于 Go 的一些注意事项:

  • 从一种混凝土类型到另一种混凝土类型都有转换
  • Go 以表达式中没有隐式转换而闻名,但在某些赋值场景中存在隐式转换。