要学 Go 的赶紧上车

2020-10-03 12:59:17 +08:00
 kidlj

Go 预计在 2021 年开始支持范型,那个时候代码对新手的复杂度将提升一个量级。当前 Go 语言只有一种明显的抽象,就是 interface (接口),范型是另一种,而且比接口要更难以理解和编写、阅读。

2017 年我决定转 Go 的时候,调查了社区的成熟度,比如 kafka,redis,mongo,mysql,pg 等等中间件的库是否完善,那时候这些该有的都已经有了。现在又过了三年,Go 的社区更加成熟了,特别是 Docker 和 Kubernets 主导的云原生生态的崛起,Go 语言占了半壁江山还多。

前一阵坐我背后的一个写 Python 的同事在学习 Go,问我一个简单的问题。一个函数的返回值是 error 类型,被他当作是返回变量,因此代码看不懂。这可能是只写过动态类型语言( python, javascript, php 等)都要面对的一个思维转变。这个时候学习 Go,除了静态类型的思维转变以及 interface 这一层抽象,你会发现 Go 大部分时候像一个动态类型的语言,而且特性非常少(比 Python 少得多),Go 开源代码和标准库也非常 accessible 。

不过当范型推出来以后,虽然从 draft design 来看范型的设计已经非常简单,但对于没接触过静态类型语言的同学来说这是一个不小的挑战,甚至函数的签名都难以分辨。因此这是一个肉眼可见的复杂度提升。而且可以预计的是,当范型可用的时候,社区里大量的开源项目和库将迁移到范型的实现(因为代码更紧凑和通用),我觉得那个时候代码不会像当前一样 accessible 。

所以这个时候上车,用大约半年到一年时间熟悉 Go 的静态类型和 interface,等到范型推出的时候,可以比较轻松地过渡过去。

下面是一些学习资料的推荐:

  1. <The Go Programming Language> 是 Brian Kernighan 和一位 Go 开发组成员 Alan 合写的一本书。虽然这本书出来好几年了,但是 Go 从 1.0 以后就没怎么变化,所以还是很适用。推荐阅读英文版,中文版在大部分时候翻译得不错,但是在一些难以理解的部分,翻译并不能让事情更容易理解,甚至还会出现错误。
  2. Web 框架推荐 Echo 。写过 Node.js 的同学会发现,Echo(或 gin ) 就是 Express 或 Koa 的翻版,都是 middleware 和 c.Next 的执行方式,属于极简框架,很容易上手。
  3. Go 的并发很简单,只有 Goroutine 和 channel 一种方式。官方出的那本书里讲解得非常清晰,必读。不过一开始如果理解起来有困难的话,甚至可以跳过,因为很多时候不太用得着。比如用 Echo 框架来写业务,大部分时候不涉及并发,并发是在 Echo 框架的层面实现的(一个请求一个 goroutine ),所以业务代码就不需要并发了。
  4. Go modules 很好用。新推出不久的 pkg.go.dev 可以查询某个库被哪些开源项目 import,可以方便地学习这个库的使用方式。比如想看哪些开源项目使用了 Echo 框架,点开 Echo 的 import by tab 就看到了。这里是示例: https://pkg.go.dev/github.com/labstack/echo/v4?tab=importedby

我写 Go 有两年时间,肯定不算是一个 Go 的高手,但也不害怕去阅读一些大型项目的代码,比如 Kubernetes 。这就是 Go 的魔力,very accessible 。就像上面说的,当范型被大量运用以后,难度应该要提高一个量级。这是我的一点点经验,分享给大家,希望有帮助。

24608 次点击
所在节点    Go 编程语言
180 条回复
neoblackcap
2020-10-05 17:36:26 +08:00
@sharpy 你自己实现的,人家可不一定会用。这是语言生态的的争论。社区不会一定会用非标准库的东西。
reus
2020-10-05 19:34:35 +08:00
@Nugine0 “跨越库的边界时,还得老老实实地判断错误”,大错特错。
说了半天,你都不知道别人怎么用的。
例如使用标准库来写一个复制文件内容的函数,可以这样写:
func CopyFile(path string, w io.Writer) (err error) {
defer catch(&err)
f, err := os.Open(path)
check(err)
defer f.Close()
_, err := io.Copy(w, f)
check(err)
return nil
}
有“if err != nil”吗?没有。就是这么简单的一件事情。

这种处理错误的方式,本来就没有说错误不是用 error 返回,也不会将抛出 panic 作为接口的一部分。
你扯什么 try/catch ?你该不会以为说用 panic/recover 处理错误,就等于不要 error 返回值吧?
我看你根本对于“现有机制”没有足够的了解。

阻止你使用这种方式来处理错误的,只是你的偏见,而不是什么共识。
你根本不用管别的库怎么处理错误,因为大家都是用 error 返回的。
在你的代码里,你喜欢写 if err != nil 就写,不喜欢写就可以用上面的方式,根本不涉及跨库。

“go 稍微给点语法糖都不会被喷 if err != nil,但还是坚持,估计以后也会一直被喷。”
喷子永远有喷的理由,一个理由站不住脚了,就找另一个。
你看喷包管理的不见了,喷没有泛型的也不见了。
当然,也有角度清奇的,“内卷工具”,佩服,这都想得出。

“以前还有 go 不需要泛型的说法”,这也是由来已久的偏见和误解,官方的态度从来都不是不需要,而是没发现好的实现方式。直到现在有了泛型的提案,对于具体的实现方式,也还没有确定,还在讨论: https://groups.google.com/g/golang-dev/c/OcW0ATRS4oM/m/5-kMv9i-BQAJ
monkeyWie
2020-10-05 19:39:48 +08:00
go 确实好用,但是错误处理是缺点这个就别洗了吧
Nugine0
2020-10-05 21:44:14 +08:00
@reus
把 if err != nil 换成 check(err),还是填一发打一枪。写个函数就不是“判断错误”了?真搞笑。

你敢把 panic 作为普通错误,作为接口的一部分,抛给库的使用者吗?如果不敢,就说明 panic/recover 不是错误处理的正解,如果敢,那祝你好运,别被 issue 挂起来。

如果未来喷 if err != nil 的不见了,那真是一件好事,这说明 go 终于拿出了好用的错误处理方案。

我说 go 的现有机制不行,需要新语法解决。你不肯接受,我还能咋办,难道顺着网线去 git push -f ?
我不会再浪费时间了,到此为止。
reus
2020-10-05 22:34:19 +08:00
@Nugine0 继续更新你转移话题的历史:
“go 稍微给点语法糖都不会被喷 if err != nil,但还是坚持,估计以后也会一直被喷。”
告诉你可以用函数替代了
“写函数替代 if err != nil 不能改变控制流,用 panic/recover 又与你之前的言论矛盾,我很好奇你是怎么写的。”
告诉你怎么写了
“用 panic/recover 来处理 error 就是一种混淆的做法,不然为什么不设计成 try/catch ?”
告诉你官方也推荐这么用了
“能用不代表它就是好的。”
呵呵,连万能句式都出来了
“语法糖不等于函数”
明明你不想写 if err != nil,那现有的机制就能解决,怎么变成非语法糖不可了?
这就是明显的转移话题,试图把话题从“if err != nil”,转移到“语法糖”
“把 if err != nil 换成 check(err),还是填一发打一枪。写个函数就不是“判断错误”了?真搞笑。”
函数写给你了,怎么改变控制流也告诉你了,这里的 panic 不会跨越包边界也告诉你了,现在忽然又转移话题,用函数判断错误就不行了??

看看你前面的论点,还有多少站得住脚吧。
也难怪你不停转移话题,不就是想让人忘记你前面说过的话嘛,让人忘记你的错误嘛。
所以我就要复制出来,免得跑题了。

我写的 ReadFile,很明显就有一个 error 返回值,我说用 panic/recover 做错误处理,从来就没有说要抛出 panic 给调用者,你以为用 panic/recover 处理错误,就等于抛 panic 给调用者,这是你的错误理解。
你以为是函数实现者用 panic 抛出,调用者用 recover 接住?
不是的,panic/recover,都是函数实现者一侧调用的,上面的 check 里用了 panic,catch 里用了 recover,这一对是在同一个函数里用的,recover 是用来将 panic 抛出的错误,赋值给 error 返回值用的。
根本不是你理解的那样,将 panic 抛给调用者。

这都还不明白的话,那我也确实没法再说什么,你水平太低。
leavic
2020-10-05 22:37:09 +08:00
我还是第一次看到有一种语言是这样逼迫别人赶紧学它的:你丫赶紧的,明年你就学不会我了。
ericgui
2020-10-06 00:43:27 +08:00
@leavic +1
beidounanxizi
2020-10-06 01:46:57 +08:00
go 还是值得学习 至于你说的泛型带来的优势 不见得
我不喜欢 go 的泛型,因为 JAVA 已经有了
go 比较讨喜的就是多返回值和 go 的函数第一公民了
看到 spring 框架满屏的 aop 贼恶心
beibeijia
2020-10-06 02:34:24 +08:00
把 "if err != nil" 改成 "$" 就完美了(手动 doge
273579540
2020-10-06 07:59:24 +08:00
www.learnfk.com/course-go 推荐一个页面教程
wwwicbd
2020-10-06 23:57:09 +08:00
刚开始练习 Golang, 写 Web APP 用官方的 http 包就基本足够了.
自己抽取了一点常用功能做了几个包, 请各位批评指点:

1. RESTFul 的路由
https://github.com/icbd/go_restful_routes

支持路径直接匹配, 路径前缀匹配, 路径参数匹配和捕获, 正则匹配. 还有静态文件的路由重定向.

2. 默认值填充
https://github.com/icbd/default_box

Golang 的结构体会自动填充零值, 但是很多时候我们想让他填充非零值的默认值. 利用了反射和 tag 实现了默认值的填充.
necodba
2020-10-07 11:33:50 +08:00
好了好了都是大佬吵架,喜闻乐见的语言之争,我就说一句,PHP 才是最好的语言
mamahaha
2020-10-08 11:40:09 +08:00
想给人打工就都得学,想自己玩可以根据项目需求去选择。
AlexSLQ
2020-10-15 14:36:41 +08:00
quxiangxuanqxx
2020-11-15 11:32:22 +08:00
大佬,我有个疑问,你说 echo 框架层面实现了每个请求是一个 goroutine

但是我看了下代码,框架层面并没有用 goroutine

应该是 net/http 实现的是每个请求是一个 goroutine 吧

ref:
https://github.com/golang/go/blob/master/src/net/http/server.go#L3304
kidlj
2020-11-15 12:30:58 +08:00
@quxiangxuanqxx 嗯,你说得对,我说错了。
b00tyhunt3r
2020-11-17 09:42:28 +08:00
谢谢大熊弟,想问一下
“Go 的并发很简单,只有 Goroutine 和 channel 一种方式。官方出的那本书里讲解得非常清晰,必读。”
那本书是指?
kidlj
2020-11-17 10:04:32 +08:00
@b00tyhunt3r <The Go Programming Language> Go 程序设计语言。
b00tyhunt3r
2020-11-17 10:15:33 +08:00
@kidlj 🙏
jmyz0455
2020-11-26 10:39:41 +08:00
我还没接触过 Go,有个疑问,就是 Go 现在还没有写 Web 的全家桶吗?开箱即用的 CURD 库也少?

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://yangjunhui.monster/t/712344

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX