1. gofmt:Go 语言在解决规模化问题上的一个最佳实践

在 Go 语言诞生和推广的初期,也许出现过因 gofmt 所格式化出来的统一代码风格与某个开发人员自己喜好的风格不一致而导致抱怨的情况,但随着 Go 影响力的扩大以及大量采用 gofmt 标准代码风格代码的累积,Go 开发者们渐渐意识到以往在其他编程语言中的那种针对代码风格的“争吵”渐渐少了甚至是消失了。在一致的代码风格下,Go 开发人员阅读和维护他人代码时感觉不再陌生,也变得更有效率了,gofmt 的代码风格成为了所有人能接受的共同的最爱,甚至于在 Go 的世界里代码风格已经变得没有了“存在感”。

  • 作为 Go 开发人员,请在提交你的代码前使用 gofmt 格式化你的 Go 源码。

2. 使用 gofmt

  1. $ gofmt -help
  2. usage: gofmt [flags] [path ...]
  3. -cpuprofile string
  4. write cpu profile to this file
  5. -d display diffs instead of rewriting files
  6. -e report all errors (not just the first 10 on different lines)
  7. -l list files whose formatting differs from gofmt's
  8. -r string
  9. rewrite rule (e.g., 'a[b:len(a)] -> a[b:]')
  10. -s simplify code
  11. -w write result to (source) file instead of stdout

一些实用的用法:

  • 使用 gofmt -s 选项简化代码
  1. // 例子
  2. for _ = range v { ... }
  3. 变为
  4. for range v { ... }
  • 使用 gofmt -r 执行代码“微重构”

格式:

  1. gofmt -r 'pattern -> replacement' [other flags] [path ...]

gofmt -r 的原理就是在对源码进行重新格式化之前,搜索源码是否有可以匹配 pattern 的表达式,如果有,将所有匹配到的结果替换为 replacement 表达式。gofmt 要求 pattern 和 replacement 都是合法的 Go 表达式。

  1. gofmt -r 'a[3:len(a)] -> a[3:]' -w main.go

上面 gofmt -r 命令执行的意图就是将源码文件 main.go 中能与 a[3:len(a)] 匹配的代码替换为 a[3:],然后再做重新格式化。注意:上述命令中的 a 并不是一个具体的字符,而是代表的一个通配符。出现在‘pattern -> replacement’的小写字母都会被视为通配符。因此上面的命令对下面的源码片段都可以成功匹配:

  1. - fmt.Println(s[3:len(s)])
  2. + fmt.Println(s[3:])
  3. - n, err := s.r.Read(s.buf[3:len(s.buf)])
  4. + n, err := s.r.Read(s.buf[3:])
  5. - reverseLabels = append(reverseLabels, domain[3:len(domain)])
  6. + reverseLabels = append(reverseLabels, domain[3:])

我们也可以将 pattern 中的 3 改为一个字母 b(通配符):

  1. gofmt -r 'a[b:len(a)] -> a[b:]' -w xxx.go
  2. - fmt.Println(s[3:len(s)])
  3. + fmt.Println(s[3:])
  4. - n, err := s.r.Read(s.buf[s.end:len(s.buf)])
  5. + n, err := s.r.Read(s.buf[3:])
  6. - reverseLabels = append(reverseLabels, domain[i+1:len(domain)])
  7. + reverseLabels = append(reverseLabels, domain[i+1:])
  • 使用 gofmt -l 按格式要求输出满足条件的文件列表

输出 $GOROOT/src 下面所有不满足 gofmt 格式要求的文件列表(以 go 1.12.6 版本为例):

  1. $ gofmt -l $GOROOT/src
  2. /home/tonybai/.bin/go1.12.6/src/cmd/cgo/zdefaultcc.go
  3. /home/tonybai/.bin/go1.12.6/src/cmd/go/internal/cfg/zdefaultcc.go
  4. /home/tonybai/.bin/go1.12.6/src/cmd/go/internal/cfg/zosarch.go
  5. /home/tonybai/.bin/go1.12.6/src/cmd/go/testdata/src/badpkg/x.go:1:1: expected 'package', found pkg
  6. /home/tonybai/.bin/go1.12.6/src/cmd/go/testdata/src/notest/hello.go:6:1: expected declaration, found Hello
  7. /home/tonybai/.bin/go1.12.6/src/cmd/go/testdata/src/syntaxerror/x_test.go:3:11: expected identifier
  8. /home/tonybai/.bin/go1.12.6/src/go/build/zcgo.go

也可以将-r 和-l 结合使用,输出匹配到 pattern 的文件列表,比如:查找 $GOROOT/src 下面能匹配到’a[b:len(a)]’ pattern 的文件列表:

  1. $ gofmt -r 'a[b:len(a)] -> a[b:]' -l $GOROOT/src
  2. /home/out1/.bin/go1.12.6/src/bufio/scan.go
  3. /home/out1/.bin/go1.12.6/src/crypto/x509/verify.go

注意:

  • 如果某路径下有很多不符合 gofmt 格式的文件,这些文件也会被一并输出。

3. 使用 goimports

gofmt 工具无法自动增加或删除掉文件首部的 package 导入列表。为此,Go 核心团队的 Brad Fitzpatrick 实现了goimports 工具,后该工具移到官方 golang.org/x/tools/cmd/goimports 下维护。

  • goimports 在 gofmt 的功能的基础上,增加了对 package 列表的维护功能,可根据源码的最新变动自动从导入包列表中增删包。

安装 goimports:

  1. $ go get golang.org/x/tools/cmd/goimports
  1. $ goimports -help
  2. usage: goimports [flags] [path ...]
  3. -cpuprofile string
  4. CPU profile output
  5. -d display diffs instead of rewriting files
  6. -e report all errors (not just the first 10 on different lines)
  7. -format-only
  8. if true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.
  9. -l list files whose formatting differs from goimport's
  10. -local string
  11. put imports beginning with this string after 3rd-party packages; comma-separated list
  12. -memprofile string
  13. memory profile output
  14. -memrate int
  15. if > 0, sets runtime.MemProfileRate
  16. -srcdir dir
  17. choose imports as if source code is from dir. When operating on a single file, dir may instead be the complete file name.
  18. -trace string
  19. trace profile output
  20. -v verbose logging
  21. -w write result to (source) file instead of stdout

4. 将 gofmt/goimports 与编辑器工具集成

Goland