【问题标题】:How to cast to a type alias in Go?如何在 Go 中转换为类型别名?
【发布时间】:2013-11-03 20:07:45
【问题描述】:

this playground snippet

相关代码:

type somethingFuncy func(int) bool

func funcy(i int) bool {
    return i%2 == 0
}

var a interface{} = funcy

func main() {

    _ = a.(func(int) bool)  // Works

    fmt.Println("Awesome -- apparently, literally specifying the func signature works.")

    _ = a.(somethingFuncy)  // Panics

    fmt.Println("Darn -- doesn't get here. But somethingFuncy is the same signature as func(int) bool.")
}

第一个强制转换通过显式声明类型起作用。但第二个演员惊慌失措。为什么?有没有一种干净的方法可以转换为更长的 func 签名?

【问题讨论】:

  • somethingFuncy 不是 funcy(i int) bool 的类型别名。这是一个全新的类型,定义为funcy(i int) bool。就像定义type A struct {Foo string}type B struct{Foo string} 一样。它们是不同的。

标签: types casting go


【解决方案1】:

2017 年更新:

使用 Go 1.9 中的类型断言,您只需在定义类型的位置添加 =

type somethingFuncy = func(int) bool

这告诉编译器somethingFuncyfunc(int) bool 的替代名称。

【讨论】:

  • 请注意,如果这样做,您将无法向新类型添加函数,它只会是别名。你会得到一个恼人的cannot define new methods on non-local type 错误。或者至少我在尝试使用基于字符串的自定义类型时做到了。
  • 对旧答案的旧评论,但为了子孙后代:它现在完全可以工作,你可以添加方法,这是我经常使用的模式
  • go1.15 仍然出现错误:cannot define new methods on non-local type
  • @SamHughes: No you can't
  • @NickMatteo,断言是:“在 go@latest 上,可以首先定义一个类型,它是在不同包中定义的类型的别名,然后在该别名上添加方法type.",我将整数类型别名为近似枚举,通常使用 value() 方法转换为别名类型以进行比较。我知道 go@1.16 及更高版本可以做到这一点,我很惊讶 go@1.15 不支持这种模式。来自github.com/golang/go/issues/23042,我看到了@griesemer griesemer added this to the Go1.11 milestone on Dec 7, 2017 这条线,罗伯特竖起了大拇指。
【解决方案2】:

我相信类型别名是你想要的。提案被接受并且应该在 Go 1.9 中。即。

TypeSpec = identifier [ "=" ] Type .

参考
https://github.com/golang/go/issues/18130
https://github.com/golang/proposal/blob/master/design/18130-type-alias.md

【讨论】:

    【解决方案3】:

    tl;博士

    对于类型断言(您使用),只有实际类型很重要。所以somethingFuncy 只等于somethingFuncy 而不是func(int) bool

    说明

    首先,这与强制转换无关。 go中没有强制转换。 有type assertionstype conversions

    您正在处理类型断言并假设相同的条件成立 至于类型conversions。我在阅读您的问题时犯了同样的错误,但实际上行为存在巨大差异。

    假设您有两种类型,例如 inttype MyInt int。这些都是可转换的,因为它们都是 共享相同的底层类型(转换规则之一),所以这有效(play):

    var a int = 10
    var b MyInt = MyInt(a)
    

    现在,假设a 不是int 类型而是interface{} (play) 类型:

    var a interface{} = int(10)
    var b MyInt = MyInt(a)
    

    编译器会告诉你:

    无法将 (type interface {}) 转换为 MyInt 类型:需要类型断言

    所以现在我们不再进行conversions,而是assertions。我们需要这样做(play):

    var a interface{} = int(10)
    var b MyInt = a.(MyInt)
    

    现在我们遇到了与您的问题相同的问题。此断言因恐慌而失败:

    panic:接口转换:接口是 int,而不是 main.MyInt

    原因在规范的type assertions section 中有说明:

    对于接口类型的表达式 x 和类型 T,主表达式 x.(T) 断言x 不是nil 并且存储在x 中的值是T 类型。符号 x.(T) 称为类型断言。 更准确地说,如果T 不是接口类型,x.(T) 断言动态 x 的类型与 T 的类型相同

    所以int 必须与MyInt 相同。 type identity 的规则规定 (除其他规则外):

    如果两个命名类型的类型名称源自同一个 TypeSpec,则它们是相同的。

    intMyInt 有不同的声明 (TypeSpecs),它们不相等 并且断言失败。当您将a 断言为int 时,断言有效。 所以你正在做的事情是不可能的。

    奖金:

    实际检查发生在in this code,它只是检查两种类型是否都 和预期的一样。

    【讨论】:

    • 我无法理解if their type names originate in the same TypeSpec。你能给我举个例子吗?谢谢。
    • TypeSpec definition 中有一些例子。在答案中,我使用了 MyInt,它的 TypeSpec MyInt int。因此MyInt 等于MyInt 但不等于type MySecondInt int 因为MySecondInt int 不等于MyInt int(名称不同)。
    • 很好的解释。我正在研究为从另一个包导入的结构创建别名。我只是在做type TreeNode tree.Node,但是当结构成员(即Node.Left)被分配一个值/地址时,它给出了need type assertion错误。这个解释让我知道了如何实际使用别名,现在在将type TreeNode tree.Node 更改为type TreeNode = tree.Node 之后,它按预期工作。感谢您的详细解释。
    【解决方案4】:

    只是为了完成 nemo 的精彩回答,请注意,虽然您不能直接从给定动态类型(例如,int)的界面(ef,interface{})跳转到另一种类型(例如,type MyInt int ),您可以依次执行这两个步骤:

    • 断言变量的动态类型是您所期望的;
    • 将该断言的结果转换为您选择的类型。

    请注意,由于底层类型,顾名思义,是动态,因此最好测试类型断言是成功还是失败。 另一方面,类型转换的正确性是由编译器强制执行的。

    这是您的 Playground sn-p 稍作修改:http://play.golang.org/p/FZv06Zf7xi

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-27
      • 2015-11-22
      • 2013-07-06
      • 2019-06-21
      • 2021-11-21
      相关资源
      最近更新 更多