【问题标题】:How can I write a function that takes either one of many types in go?我如何编写一个函数来接受多种类型中的任何一种?
【发布时间】:2016-03-28 04:16:18
【问题描述】:

我正在尝试一个小项目并尝试编写这些函数:

func fatal(reason string) {
    println(reason)
    os.Exit(1)
}

func fatal(err error) {
    fatal(err.Error())
}

经过一番挖掘并找到this answer,它引用了the docs on overloading,我意识到我试图做的事情在go中是非法的。

我想要的是一个简单的 api,它允许我使用字符串或错误来调用 fatal 以简化我的逻辑。我如何实现这个或类似的目标?

func fatal(reason string)func fatalErr(err error) 一起使用会感觉不雅,这是需要的吗?我是否错过了语言的另一个功能,可以让我做我想做的事?

【问题讨论】:

    标签: go types


    【解决方案1】:

    最常见的方法是将方法定义为func fatal(err interface{}),然后进行类型断言或在其主体中使用类型开关来处理每种不同的类型。如果我正在为您的示例进行编码,它将如下所示;

    func fatal(err interface{}) {
         if v, ok := err.(string); ok {
             fmt.Println(v)
         }
         if v, ok := err.(error); ok {
              fmt.Println(v.Error())
         } else {
            // panic ?
         }
    }
    

    还有;这是有关类型开关和断言的快速阅读,可能会有所帮助; http://blog.denevell.org/golang-interface-type-assertions-switch.html 您还可以查看 Effective-go,因为它包含两个功能的部分。

    【讨论】:

    • 类型开关和类型断言。谢谢,这看起来正是我想用谷歌搜索的。 :)。我们需要通过将类型信息强制转换为空接口来丢失类型信息,这似乎很遗憾。有什么方法可以创建一个字符串或错误类型,并说我们的致命函数采用这种类型的东西?
    • @MikeH-R 你可以声明一个 string 和 err 都实现的接口。不过,您的用例有点奇怪。 error 实际上是一个接口,它的唯一方法是Error(),它返回您要显示为错误的字符串。这是另一个想法...对fatal 进行一个定义,它只接受error,然后当你有一个字符串并将错误传递给它时执行errors.New(theString)。如果您好奇,这里是错误包源; golang.org/src/errors/errors.go
    • 感谢埃文,非常有帮助。
    • 因为 fmt.Println 包含这个答案中逻辑的超集,所以函数可以简化为:func fatal(v interface{}) { fmt.Println(v); os.Exit(1); }
    • @CodingPickle 好更新。正如所指出的,fmt.Println 检查类型是否为错误,如果是则打印input.Error(),因此如果您要处理的两种类型是字符串和错误,则实际上不需要键入断言或执行任何操作。话虽如此,问题是如何编写一个接受多种类型的函数,所以我提供了一个通用的答案来解释如何在 Go 中处理这些场景,而不仅仅是一个示例来证明在这种特定情况下缺乏需求。跨度>
    【解决方案2】:

    改用 log.Fatal()。 https://golang.org/pkg/log/#Fatal

    您可以使用 interface{},但不建议这样做,因为这样做会失去类型检查的所有好处。 Go 作者开始使用 interface{},因为他们了解在使用 interface{} 时要执行的适当级别的附加测试和检查。当需要这样的东西时,使用内置和标准库函数要容易得多(即使是中级和高级地鼠)。

    Go 也没有代数或/和类型。标准的解决方法是使用指针定义一个和/产品类型(例如 struct{*string, *error}),并努力确保您在任何时候都只将其中一个字段设为非 nil。

    【讨论】:

    • 嗨,谢谢@voutasaurus。我只是从使用log.Fatal 转移到其他东西,它几乎 完全符合我的要求,但我对它打印的时间戳感到恼火,所以写(或试图写)一个辅助函数那是相似的。 (编写一个小型命令行应用程序和时间戳感觉没有必要)。感谢您提供有关 OR 类型的提示和信息。 (再说一次,你永远无法在其他地方获得像 haskell 一样好的类型系统,叹息)。
    • 您可以定义自己的记录器,不会在一行中打印时间戳:play.golang.org/p/TLMxtCag_0
    • 哈哈哈,你说得对!为我服务,因为我一开始就没有阅读日志包。哦,好吧,至少我学到了一些东西。
    • ... 或者您可以更改默认记录器上的标志以抑制时间戳:log.SetFlags(0).
    【解决方案3】:

    该语言不支持函数重载。从 Golang 官方网站上可以看到,

    如果方法分派也不需要进行类型匹配,则它会被简化。使用其他语言的经验告诉我们,具有相同名称但不同签名的各种方法有时很有用,但在实践中也可能令人困惑和脆弱。仅按名称匹配并要求类型的一致性是 Go 类型系统中一个主要的简化决定。 关于运算符重载,它似乎比绝对要求更方便。同样,没有它,事情会变得更简单。

    https://golang.org/doc/faq#overloading

    一种可能的解决方案是定义一个高级函数来检查和处理不同类型,类似于重载多个函数的方式。请参阅@evanmcdonnal 的解决方案以获取一个很好的示例。

    【讨论】:

    • 我在我的问题中链接到了那个。 :)
    • 您也没有提供任何形式的答案,只是陈述事实。尝试将这个想法扩展到你将如何在 Go 中处理这个问题,而不是仅仅声明你不能重载方法。
    • @evanmcdonnal 我正在对您所拥有的示例进行高级解释。在电话上打字并不是最有效的回答方式。为您的回答 +1。
    • 啊,谢谢。抱歉,如果该评论措辞粗鲁。旨在具有建设性。
    猜你喜欢
    • 2011-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多