go 函数末尾缺少返回值_伙计们,Go 并没有那么简单?你觉得呢

本文的观点,你认同吗?

cf3f8dc32e38e66c6fe8ac15eb048345.png

出于好奇,我最近开始接触一些 Go 的代码。我之前对它有一些了解,但是从来没有尝试去写(没有需求)。但是现在我们团队选择使用 Go 来开发一个项目,所以我觉得这是一个获得实际经验的好机会。

到目前为止,关于这门语言我已经学习了很长时间。在这个博文的末尾,我会写更多关于 Go 的干货。

社区实际上并不那么令人愉快,特别是那些因为它的简单性而主张使用 Go 的人。似乎简单已经成为 Go 社区中的一个流行语,许多人反复重复提到这点,却没有给出太多实际的想法。

这对我来说似乎很不幸,因为在我看来,Go 是一个“极其简单的语言”:

  1. 不应该作为考虑使用 Go 的主要原因
  2. 从他们的关注点中找到其他更有利的推荐理由
  3. 甚至不是真的(不是真的简单)

在这篇文章中,我想围绕 Go 来分析一些简单观点。

在深入之前,我想强调一件事情:这篇文章并不是对 Go 的批评,而是一种对 Go 的宣传和倡导的方式。有时候,我可能会批评这个语言的某个方面,但这不是我们关注的重点,我只会试图用一种非正式的、事实的,每种语言都会涉及的方式来讲述。

我来自哪里

出于工作和业余爱好,我同时使用多种编程语言。我不赞成有“最喜欢的语言”的概念。过去我曾经有过一些最喜欢的语言,但这种认识往往是一时的情感,随着时间推移,会发生变化。

在我的工作中,我使用 C++ 和 Python 写大型服务的后端代码。过去我曾经在一个你可能知道的操作系统上工作,而且我也做了嵌入式工作。在业余项目中,我做了其他各种事情。

我并不是夸耀什么(我不是一个专家),我只是想表明,我在编程的许多领域至少有一些见解,而且我一直努力保持开放的心态。

所以,不要着急,让我们开始讨论正题,看看几个观点。

1. “与主流语言相比,Go 的关键字非常少”

我从一个最常见的例子开始。当推广 Go 时,这会是大家的口头禅。

首先,即使它是真实的,我不知道为什么关键字数量会是判断一个语言的学习曲线或复杂性的重要依据。当然,如果有成千上万的关键字,这可能是一个问题。但是大多数语言最多只有几十个关键字,这种规模下,关键字的多少是无关紧要的。

我还没有听到有人因为关键字的数量而抱怨某门语言

其次,Go 所谓的“很少”的关键字实际上只不过是一个聪明律师的伎俩(也许,我甚至会认为这是 Go 的虚假广告)。Go 规范 列出了 25 个关键字,这的确比大多数语言要少些。但在我看来,Go 并没有比其他语言关键字表示更少的概念,Go 虽然没有这些关键字,但相应的概念依然是语言的一部分(即实际的复杂性保持不变)。

为了说明我的意思,请考虑一个 while 循环。 Go 没有这个关键字,这是真的,但它仍然有一个 while 循环,文档甚至是这样说的,它的目的只是重用其他关键字。

另一个这样的例子是 private 和 public。 Go 没有这些关键字,但它仍然有 private 和 public,它只是使用字母大小写而不是关键字。

用来删减关键字的另一个技巧叫 预定义标识符(Predeclared identifiers),在技术上它不是关键字,但是在实践中仍然需要它们,创建一个和它同名的变量仍然不是一个好主意,因此,最后看来...它们基本上是关键字。此外,其中一些预定义标识符是其他语言的关键字,因此仅将它们与 Go 的关键字列表进行比较是非常不公平的。就像苹果和桔子。

2. 接收者参数

接受者参数对我来说有些古怪。看起来 Go 似乎并不建议使用 this 和 self,但是仍然需要方法,所以就存在 “接收者参数”,除了方法签名看上去很奇怪之外,它们基本上是一样的。

接收者参数有一个问题,当访问一个方法时,我需要知道接收者参数(这是任意的)的名称,以明确这个方法的作用。因为缺少关键字(译注:如 this),语法高亮成为一个问题。(看吧?这是如何减少关键字实际上使事情变得更加复杂的例子。)这有点像 C++ 中的隐式 this。

这里有一个新人容易混淆的例子

恕我直言,最简单、最直接的方式来表达一个接收器是 UFCS,而不是 C++ 或 Go 的方式。但就像我说的,我不是在抱怨 Go,我真的不介意接受者参数的观点(如果我忍受不了 C++ 的怪异,我可以忍受 Go 的)。

3. 函数返回值

如果接收参数不够,函数甚至能够通过各种形式的返回值来声明。通常语言允许你通过 return 语句返回函数中的一个值。而在 Go 语言中,你可以返回多个值(我认为可以用更优雅的方式通过元组来解决,但是就这样吧)。除此之外,还有命名返回值。在我看来,并不是一个好主意,因为它允许我们在那些很难找到返回值的地方写上晕头转向的代码。结合接收方参数,您可以创建这样的函数签名:

func (f Foobar) Something(a int, b int, c int) (foo int, bar int) { // ...}

这是有效的 Go 代码。如您所见,有三个参数。我真的不希望任何人试图选择这个“简单”,因为这个语法除了简单,什么也不是。

4. “没有继承”

Go(或许只是社区)似乎很反对“传统的 OOP”(不管这是指哪个,可能是 Java 或者 C++),我记得有人说 Go 没有继承是一件好事。

除此之外,Go 有一个功能叫做嵌入,这个文档以及一些博客文章声称 Go 没有继承。我试着用各种方式使用它,我没法认为 Go 反对继承。上面链接的文档说:

还有种区分内嵌与子类的重要手段。当内嵌一个类型时,该类型的方法会成为外部类型的方法,但当它们被调用时,该方法的接收者是内部类型,而非外部的。

有差别吗?继承通常以相同的方式工作,继承的方法也对内部类型起作用。

在我看来,在 Go 中,真正唯一不同的是,多态性从结构中解耦。你需要使用接口来使用多态性。但一旦你做了,做的事情和传统的 OOP 非常相似,包括方法覆盖 - 这里是个演示

关于 Go,有件事令我很惊讶 —— 这门所谓简单的语言 —— 你甚至可以实现多重继承。确实很糟糕。 golang-nut 的邮件列表中,有人提到,Go 并不能很好的处理继承的歧义。我已经调整了其中提及的代码,以便它展示了著名的“可怕的钻石问题”(Dreaded diamond problem):

package mainimport "fmt"type T1 struct { T2 T3}type T2 struct { T4 foo int}type T3 struct { T4}type T4 struct { foo int}func main() { t2 := T2{ T4{ 9000 }, 2 } t3 := T3{ T4{ 3 } } fmt.Printf("foo=%d
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值