data source name

[username[:password]@][protocol[(address)]]/dbname[?param1=value1&...¶mN=valueN]
大概长这样:
username:password@protocol(address)/dbname?param=value

为了方便定义数据库源的一种格式化的字符串,
程序用起来还是又要parse又要format的(看下面参考链接里的go实现,复杂+不爽),
也许觉得对人类友好吧,
真的友好吗?

参考链接

https://github.com/go-sql-driver/mysql#dsn-data-source-name
https://github.com/go-sql-driver/mysql/blob/66312f7fe2678aa0f5ec770f96702f4c4ec5aa8e/dsn.go#L246

go语言测试

  • 使用_test做为包名做黑盒测试, 使用import . xxx导入要测试的包, 有时为了打破循环引用也需要用这种方式
  • t.Skip(), 当检测到有些测试条件不满足时(比如外部依赖,环境变量没设等情况)可以跳过这个case
  • go test -short, TestCase里用testing.Short判断用户使用了-short参数时,可以做判断跳过耗时的case
  • go test -timeout 1s, 指定耗时, 超时就失败
  • go test -run TestNameRegexp 只执行指定的测试用例
  • t.Parallel() 标记为可以并行测试, 在Test case函数体一开始就调用

参考链接

https://splice.com/blog/lesser-known-features-go-test/

go语言槽点

想起一条写一条,
之后尽量补上例子和理想的做法

定义类型的地方允许x, y, z int这种写法表示x, y, z均为int型

可读性不好

没有重载

许多package里看到大片的XXXInt, XXXInt64, XXXUint64, ...
视觉污染, 写代码麻烦, 改代码也麻烦

defer

  • 可读性不好, 本身就已经颠覆了一般性的顺序执行思维, 写在前面, 执行却在后面,
    而且多条defer在一起还是LIFO顺序, 真是counter-intuitive,
    还是像其他语言那样try finally比较好, 或者像python, C#那样有with语句更好,
  • 容易引发bug, 因为defer可以修改return语句的值, 使得return语句处具有不确定性,
    使得程序员容易搞错

var, :=, =

太容易出错了, 何必整这么多种, 而且a, b := xxx时, a, b中有一个未定义也合法, 简直dirty
编译后经常报:=前面没有新变量,然后去改成=号,许多时间就浪费在这种事情上
像python或erlang那样都是=对大家都好
平白增加程序员需要处理的细节, 与go宣传的简单化背道而驰

没有implement这样的关键字

无意中就实现了某interface{},
违背了程序员的意志,
带来意想不到的问题

closure做得不对

直接定义一个匿名函数,函数用到的外部变量并不是当时的变量快照,而是一个引用,
想要真正的closure需要用传参来做.
https://www.goinggo.net/2014/06/pitfalls-with-closures-in-go.html

一个package可以对应多个文件

函数定义还需要到别的文件里去找. import语句也重复了

没有泛型

返回error

每一层函数调用都要check一遍,到处都充斥着if err != nil
中间层函数只是因为调到了返回error的函数,自己的返回值也被迫加上error

没有条件表达式

当然这个不是什么致命伤,
但是需要的时候发现go代码写起来巨麻烦,
其实希望最好像函数式编程那样一切都是表达式,if或case等都能有一个返回值

优点

gofmt

虽然别的语言也可以一个插件搞定, 不过go这方面做得不错
由于是官方提供的, 大家就统一了