1. gofmt:Go 语言在解决规模化问题上的一个最佳实践
在 Go 语言诞生和推广的初期,也许出现过因 gofmt 所格式化出来的统一代码风格与某个开发人员自己喜好的风格不一致而导致抱怨的情况,但随着 Go 影响力的扩大以及大量采用 gofmt 标准代码风格代码的累积,Go 开发者们渐渐意识到以往在其他编程语言中的那种针对代码风格的“争吵”渐渐少了甚至是消失了。在一致的代码风格下,Go 开发人员阅读和维护他人代码时感觉不再陌生,也变得更有效率了,gofmt 的代码风格成为了所有人能接受的共同的最爱,甚至于在 Go 的世界里代码风格已经变得没有了“存在感”。
- 作为 Go 开发人员,请在提交你的代码前使用 gofmt 格式化你的 Go 源码。
2. 使用 gofmt
$ gofmt -helpusage: gofmt [flags] [path ...]-cpuprofile stringwrite cpu profile to this file-d display diffs instead of rewriting files-e report all errors (not just the first 10 on different lines)-l list files whose formatting differs from gofmt's-r stringrewrite rule (e.g., 'a[b:len(a)] -> a[b:]')-s simplify code-w write result to (source) file instead of stdout
一些实用的用法:
- 使用 gofmt -s 选项简化代码
// 例子for _ = range v { ... }变为for range v { ... }
- 使用 gofmt -r 执行代码“微重构”
格式:
gofmt -r 'pattern -> replacement' [other flags] [path ...]
gofmt -r 的原理就是在对源码进行重新格式化之前,搜索源码是否有可以匹配 pattern 的表达式,如果有,将所有匹配到的结果替换为 replacement 表达式。gofmt 要求 pattern 和 replacement 都是合法的 Go 表达式。
gofmt -r 'a[3:len(a)] -> a[3:]' -w main.go
上面 gofmt -r 命令执行的意图就是将源码文件 main.go 中能与 a[3:len(a)] 匹配的代码替换为 a[3:],然后再做重新格式化。注意:上述命令中的 a 并不是一个具体的字符,而是代表的一个通配符。出现在‘pattern -> replacement’的小写字母都会被视为通配符。因此上面的命令对下面的源码片段都可以成功匹配:
- fmt.Println(s[3:len(s)])+ fmt.Println(s[3:])- n, err := s.r.Read(s.buf[3:len(s.buf)])+ n, err := s.r.Read(s.buf[3:])- reverseLabels = append(reverseLabels, domain[3:len(domain)])+ reverseLabels = append(reverseLabels, domain[3:])
我们也可以将 pattern 中的 3 改为一个字母 b(通配符):
gofmt -r 'a[b:len(a)] -> a[b:]' -w xxx.go- fmt.Println(s[3:len(s)])+ fmt.Println(s[3:])- n, err := s.r.Read(s.buf[s.end:len(s.buf)])+ n, err := s.r.Read(s.buf[3:])- reverseLabels = append(reverseLabels, domain[i+1:len(domain)])+ reverseLabels = append(reverseLabels, domain[i+1:])
- 使用 gofmt -l 按格式要求输出满足条件的文件列表
输出 $GOROOT/src 下面所有不满足 gofmt 格式要求的文件列表(以 go 1.12.6 版本为例):
$ gofmt -l $GOROOT/src/home/tonybai/.bin/go1.12.6/src/cmd/cgo/zdefaultcc.go/home/tonybai/.bin/go1.12.6/src/cmd/go/internal/cfg/zdefaultcc.go/home/tonybai/.bin/go1.12.6/src/cmd/go/internal/cfg/zosarch.go/home/tonybai/.bin/go1.12.6/src/cmd/go/testdata/src/badpkg/x.go:1:1: expected 'package', found pkg/home/tonybai/.bin/go1.12.6/src/cmd/go/testdata/src/notest/hello.go:6:1: expected declaration, found Hello/home/tonybai/.bin/go1.12.6/src/cmd/go/testdata/src/syntaxerror/x_test.go:3:11: expected identifier/home/tonybai/.bin/go1.12.6/src/go/build/zcgo.go
也可以将-r 和-l 结合使用,输出匹配到 pattern 的文件列表,比如:查找 $GOROOT/src 下面能匹配到’a[b:len(a)]’ pattern 的文件列表:
$ gofmt -r 'a[b:len(a)] -> a[b:]' -l $GOROOT/src/home/out1/.bin/go1.12.6/src/bufio/scan.go/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:
$ go get golang.org/x/tools/cmd/goimports
$ goimports -helpusage: goimports [flags] [path ...]-cpuprofile stringCPU profile output-d display diffs instead of rewriting files-e report all errors (not just the first 10 on different lines)-format-onlyif true, don't fix imports and only format. In this mode, goimports is effectively gofmt, with the addition that imports are grouped into sections.-l list files whose formatting differs from goimport's-local stringput imports beginning with this string after 3rd-party packages; comma-separated list-memprofile stringmemory profile output-memrate intif > 0, sets runtime.MemProfileRate-srcdir dirchoose imports as if source code is from dir. When operating on a single file, dir may instead be the complete file name.-trace stringtrace profile output-v verbose logging-w write result to (source) file instead of stdout
4. 将 gofmt/goimports 与编辑器工具集成
Goland
