代码规范除了让多人协作的项目的代码风格一致,甚至能够发现一些潜在的缺陷。因此,代码规范可以认为是代码开发的最佳实践,能够大大提高研发效率。 针对 Golang 语言,继 Gometalinter 不再维护之后,GolangCI-Lint 成为最佳的代码规范检查工具。 本文将基于实例详细介绍一些最佳实践,让你的 Golang 代码更规范。
GolangCI-Lint 最佳实践
基于 GolangCI-Lint 的使用经验,总结出如下最佳实践:
- 集成到 CI:通过 CI 反复地自动检查,禁止不规范代码合入。
- 使用固定版本:由于 GolangCI-Lint 自身一直在不停迭代,使用固定版本能够保证结果的确定性。
- 使用配置文件:GolangCI-Lint 通过配置文件提供大量灵活的配置,支持的配置参考 .golangci.example.yml。使用配置文件而不是命令行参数,方便进行版本控制。
- 设置合理
GOGC
:通过设置合理的GOGC
达到内存与时间消耗的平衡,设置指导参考 Memory Usage of Golangci-lint。
Lint 常见问题及解决方案
如下演示的例子,基于 supereagle/go-example/lint 源代码。
依赖包顺序
错误提示:
main.go:4: File is not `gofmt`-ed with `-s` (gofmt)
"github.com/supereagle/go-example/lint/students"
make: *** [lint] Error 1
解法方案:
依赖包的顺序为:标准库包、第三方依赖包、本项目自定义包。执行 gofmt -s -w ${xxx.go} 自动修复。
diff --git a/lint/main.go b/lint/main.go
index eccd170..ad8ecf7 100644
--- a/lint/main.go
+++ b/lint/main.go
@@ -1,8 +1,9 @@
package main
import (
- "github.com/supereagle/go-example/lint/students"
"fmt"
+
+ "github.com/supereagle/go-example/lint/students"
)
func main() {
没有使用的代码
错误提示:
无用函数:
main.go:15:6: `unusedFunc` is unused (deadcode)
func unusedFunc() {
^
make: *** [lint] Error 1
无用结构体字段:
students/students.go:6:2: `sex` is unused (structcheck)
sex string
^
make: *** [lint] Error 1
解法方案:
diff --git a/lint/main.go b/lint/main.go
index 7cfa2b1..ad8ecf7 100644
--- a/lint/main.go
+++ b/lint/main.go
@@ -11,7 +11,3 @@ func main() {
fmt.Printf("Student: name: %s, age: %d\n", s.GetName(), s.GetAge())
}
-
-func unusedFunc() {
- fmt.Println("Remove me!")
-}
diff --git a/lint/students/students.go b/lint/students/students.go
index 19818c5..1a0d28d 100644
--- a/lint/students/students.go
+++ b/lint/students/students.go
@@ -3,7 +3,6 @@ package students
type Students struct {
name string
age int
- sex string
}
func New(name string, age int) *Students {
单词拼写错误
错误提示:
main.go:12:20: `infromation` is a misspelling of `information` (misspell)
// Output student infromation.
^
make: *** [lint] Error 1
解法方案:
纠正拼写错误的单词。
diff --git a/lint/main.go b/lint/main.go
index 8b7651f..5c9621a 100644
--- a/lint/main.go
+++ b/lint/main.go
@@ -9,6 +9,6 @@ import (
func main() {
s := students.New("Robin", 30)
- // Output student infromation.
+ // Output student information.
fmt.Printf("Student: name: %s, age: %d\n", s.GetName(), s.GetAge())
}
变量申明时类型重复
错误提示:
students/students.go:9:8: should omit type *Students from declaration of var s; it will be inferred from the right-hand side (golint)
var s *Students = &Students{
^
make: *** [lint] Error 1
解法方案:
删除赋值表达式左边的类型。
diff --git a/lint/students/students.go b/lint/students/students.go
index 14bde64..a1e2b68 100644
--- a/lint/students/students.go
+++ b/lint/students/students.go
@@ -6,7 +6,7 @@ type Students struct {
}
func New(name string, age int) *Students {
- var s *Students = &Students{
+ var s = &Students{
name: name,
age: age,
}
命名中带下划线
错误提示:
func (s *Students) Get_Name() string {
^
make: *** [lint] Error 1
解法方案:
采用驼峰格式命名。
diff --git a/lint/students/students.go b/lint/students/students.go
index 9f21a82..1a0d28d 100644
--- a/lint/students/students.go
+++ b/lint/students/students.go
@@ -12,7 +12,7 @@ func New(name string, age int) *Students {
}
}
-func (s *Students) Get_Name() string {
+func (s *Students) GetName() string {
return s.name
}
注释格式错误
错误提示:
main.go:9:1: comment on exported var `Mack` should be of the form `Mack ...` (golint)
// A smart boy.
^
make: *** [lint] Error 1
解法方案:
注释的第一个单词,应该为被注释的变量、方法、属性等的名字。
diff --git a/lint/main.go b/lint/main.go
index 7ce6ebd..10a1533 100644
--- a/lint/main.go
+++ b/lint/main.go
@@ -6,7 +6,7 @@ import (
"github.com/supereagle/go-example/lint/students"
)
-// A smart boy.
+// Mack is a smart boy.
var Mack = students.New("Mack", 20)
func main() {
导出的函数返回未被导出的类型
错误提示:
students/students.go:8:32: exported func New returns unexported type *students.students, which can be annoying to use (golint)
func New(name string, age int) *students {
^
make: *** [lint] Error 1
解法方案:
导出返回的类型。
diff --git a/lint/students/students.go b/lint/students/students.go
index 5c6b37a..1a0d28d 100644
--- a/lint/students/students.go
+++ b/lint/students/students.go
@@ -1,21 +1,21 @@
package students
-type students struct {
+type Students struct {
name string
age int
}
-func New(name string, age int) *students {
- return &students{
+func New(name string, age int) *Students {
+ return &Students{
name: name,
age: age,
}
}
-func (s *students) GetName() string {
+func (s *Students) GetName() string {
return s.name
}
-func (s *students) GetAge() int {
+func (s *Students) GetAge() int {
return s.age
}
冗余代码
错误提示:
main.go:14:2: S1023: redundant `return` statement (gosimple)
return
^
make: *** [lint] Error 1
解法方案:
删除冗余代码。
diff --git a/lint/main.go b/lint/main.go
index 9c50634..5c9621a 100644
--- a/lint/main.go
+++ b/lint/main.go
@@ -11,5 +11,4 @@ func main() {
// Output student information.
fmt.Printf("Student: name: %s, age: %d\n", s.GetName(), s.GetAge())
- return
}
If 判断返回 bool 值
错误提示:
students/students.go:24:2: S1008: should use 'return <expr>' instead of 'if <expr> { return <bool> }; return <bool>' (gosimple)
if s.age < 18 {
^
make: *** [lint] Error 1
解法方案:
直接返回 bool 表达式,不用再 if 判断。
diff --git a/lint/students/students.go b/lint/students/students.go
index cfb7b24..d1118f9 100644
--- a/lint/students/students.go
+++ b/lint/students/students.go
@@ -21,9 +21,5 @@ func (s *Students) GetAge() int {
}
func (s *Students) IsYoung() bool {
- if s.age < 18 {
- return true
- }
-
- return false
+ return s.age < 18
}
包名跟导出类型名的前缀相同
错误提示:
students/students.go:23:6: type name will be used as students.StudentsScore by other packages, and that stutters; consider calling this Score (golint)
type StudentsScore struct {
^
make: *** [lint] Error 1
解法方案:
方案一:去掉类型名中跟包名相同的前缀
diff --git a/lint/students/students.go b/lint/students/students.go
index 8733913..91ec74b 100644
--- a/lint/students/students.go
+++ b/lint/students/students.go
@@ -20,13 +20,13 @@ func (s *Students) GetAge() int {
return s.age
}
-type StudentsScore struct {
+type Score struct {
cource string
score int
}
-func NewStudentsScore(cource string, score int) *StudentsScore {
- return &StudentsScore{
+func NewScore(cource string, score int) *Score {
+ return &Score{
cource: cource,
score: score,
}
方案二:修改包名,使其跟类型名的前缀不同
diff --git a/lint/main.go b/lint/main.go
index 5c9621a..02bf513 100644
--- a/lint/main.go
+++ b/lint/main.go
@@ -3,11 +3,11 @@ package main
import (
"fmt"
- "github.com/supereagle/go-example/lint/students"
+ "github.com/supereagle/go-example/lint/student"
)
func main() {
- s := students.New("Robin", 30)
+ s := student.New("Robin", 30)
// Output student information.
fmt.Printf("Student: name: %s, age: %d\n", s.GetName(), s.GetAge())
diff --git a/lint/students/students.go b/lint/student/students.go
similarity index 99%
rename from lint/students/students.go
rename to lint/student/students.go
index 9cf942f4..fbe5b6d7 100644
--- a/lint/students/students.go
+++ b/lint/student/students.go
@@ -1,4 +1,4 @@
-package students
+package student
type Students struct {
name string