— 翻译自外文
https://pasztor.at/blog/go-is-terrible
[https://tour.golang.org/moretypes/1]

一、go不区分异常和错误,一切皆error

关于异常和错误
异常一般是不影响程序继续执行的错误,如果不抛出异常,可以继续往下执行。
错误是会导致程序正常执行的,比如outofmemory,如果不处理程序可能会终止。

go的error设计
go没有设计exception,函数只能返回一个error, error是一个interface{},也就是说go的error包含了异常和错误,并未区分;对于调用者来可以不处理这个错误,但要一个稳定运行的服务,肯定要知道是什么错和怎么处理错误。

开发者的处理
一方面ide不会要求你处理这个错误,但会提示你还有错误未处理
一方面编译器也不会强制要求处理这个错误,所以如果你不处理或忘了处理,这个错误将一直存在
当然了,错误可以通过检查源代码发现、处理或避免

理解error的难点

  • error返回一个interface,通常理解的一个error是:
    1. var MyError = errors.New("this is an error")
    一个字符串类型的错误,并不区分错误类型,所以会有下面两种处理方式
    1. if err != nil {
    2. return err
    3. }
    1. if err != nil {
    2. log.Fatalf("an error happened (%v)", err)
    3. }
    第一种是当异常往上层抛出,多了很多判断的冗余代码;第二种是记录了一个无意义的error日志,程序往下还是会报错

所以为什么不能设计成支持多类型错误的异常呢,可以减少很多冗余代码,还能减少无意义的日志记录,让程序逻辑跟清晰。

二、Nullability

nil可以适用于go支持的任何指针类型,指针是指向一块有效内存的地址,而空指针意味着没有指向任何内存块。

  1. something := getSomething()
  2. something.process()

something有可能是空指针,导致运行时出错;可以通过返回值是否为nil来判断,但意味着代码冗余了。

三、作用域和代码结构

1、到目前为止,go最大的问题是作用域。

  • go没有public、protected和private来标识变量的适用范围,只有在编译的时候才能知道是否变量可见。
  • go以小写开头的变量可以认为是private的,并且只有当前包内使用,而go一般认为一个文件夹就是一个包。当使用go module管理go项目时一个文件夹是一个包,当用bazel时可以规定多个文件夹。
  • go大写可以在全局范围内使用,但是在相同包内并没有一种方式可以区分变量可见性。

2、代码结构不佳

  • go用数据结构和一系列函数来完成业务逻辑,而没有使用类和私有变量包含数据。

四、缺少不变性

五、缺少通用性

六、差劲的OOP

七、没有枚举

八、包管理

九、不实宣传

go不太合适大量业务逻辑的项目,很难管理和维护。

总结:

go确实是一门系统仅开发的好语言。