编码规范
此开源图书由ithaiq原创,创作不易转载请注明出处
代码格式化
注意:代码都必须用 gofmt 进行格式化,文件长度800行,函数长度80行
包引用
// bad
"github.com/dgrijalva/jwt-go/v4"
//good
jwt "github.com/dgrijalva/jwt-go/v4"
//注意顺序
import (
// go 标准包
"fmt"
// 第三方包
"github.com/jinzhu/gorm"
"github.com/spf13/cobra"
"github.com/spf13/viper"
// 匿名包单独分组,并对匿名包引用进行说明
// import mysql driver
_ "github.com/jinzhu/gorm/dialects/mysql"
// 内部包
v1 "github.com/marmotedu/api/apiserver/v1"
metav1 "github.com/marmotedu/apimachinery/pkg/meta/v1"
"github.com/marmotedu/iam/pkg/cli/genericclioptions"
)
声明、初始化和定义
var (
Width int
Height int
)
// 指针初始化
// bad
sptr := new(T)
sptr.Name = "bar"
// good
sptr := &T{Name: "bar"}
//struct 声明和初始化格式采用多行
type User struct{
Username string
Email string
}
user := User{
Username: "colin",
Email: "colin404@foxmail.com",
}
//尽可能指定容器容量,以便为容器预先分配内存
v := make(map[int]string, 4)
v := make([]string, 0, 4)
//var变量声明
// bad
var _s string = F()
// good
var _s = F()
//对于未导出的顶层常量和变量,使用_作为前缀。
// bad
const (
defaultHost = "127.0.0.1"
defaultPort = 8080
)
// good
const (
_defaultHost = "127.0.0.1"
_defaultPort = 8080
)
//空格隔开
// bad
type Client struct {
version int
http.Client
}
// good
type Client struct {
http.Client
version int
}
错误处理
// bad
load()
// good
_ = load()
//error作为函数的值返回且有多个返回值的时候,error必须是最后一个参数
//尽早进行错误处理,并尽早返回,减少嵌套
//错误要单独判断,不与其他逻辑组合判断。
//错误描述告诉用户他们可以做什么must,而不是告诉他们不能做什么
fmt.Errorf("module xxx: %w", err)
//小写开头 无标点结尾
errors.New("redis connection failed")
panic使用:业务逻辑处理中禁止使用panic; 在main包中使用 log.Fatal
来记录错误。
命名规范
包名
包名必须和目录名一致
包名全部小写,没有大写或下划线,使用多级目录来划分层级
项目名可以通过中划线来连接多个单词。
包名以及包所在的目录名,不要使用复数。
不要用 common、util、shared 或者 lib 这类宽泛的、无意义的包名
包名要简单明了,例如 net、time、log
函数命名
MixedCaps或者mixedCaps 控制权限
文件名
文件名要简短有意义、文件名应小写,并使用下划线分割单词
接口命名
单个函数的接口名以 “er"”作为后缀
变量命名
特有名词(全大写)
// A GonicMapper that contains a list of common initialisms taken from golang/lint var LintGonicMapper = GonicMapper{ "API": true, "ASCII": true, "CPU": true, "CSS": true, "DNS": true, "EOF": true, "GUID": true, "HTML": true, "HTTP": true, "HTTPS": true, "ID": true, "IP": true, "JSON": true, "LHS": true, "QPS": true, "RAM": true, "RHS": true, "RPC": true, "SLA": true, "SMTP": true, "SSH": true, "TLS": true, "TTL": true, "UI": true, "UID": true, "UUID": true, "URI": true, "URL": true, "UTF8": true, "VM": true, "XML": true, "XSRF": true, "XSS": true, }
若变量类型为bool类型,则名称应以Has,Is,Can或Allow开头
局部变量应当尽可能短小 buf⇒buffer
常量命名
// Code defines an error code type. type Code int // Internal errors. const ( // ErrUnknown - 0: An unknown error occurred. ErrUnknown Code = iota // ErrFatal - 1: An fatal error occurred. ErrFatal )
error命名
Error类型应该写成XXXError的形式。 type ExitError struct { // .... } Error变量写成ErrXXX的形式。 var ErrFormat = errors.New("unknown format")
基础类型
字符串
// bad if s == "" { // normal code } // good if len(s) == 0 { // normal code }
[]byte和string比较
// bad var s1 []byte var s2 []byte ... bytes.Equal(s1, s2) == 0 bytes.Equal(s1, s2) != 0 // good var s1 []byte var s2 []byte ... bytes.Compare(s1, s2) == 0 bytes.Compare(s1, s2) != 0 strings.Compare()
空slice map、channel 判断
// bad if len(slice) = 0 { // normal code } // good if slice != nil && len(slice) == 0 { // normal code }
slice
//声明 // bad s := []string{} s := make([]string, 0) // good var s []string //复制 // bad var b1, b2 []byte for i, v := range b1 { b2[i] = v } for i := range b1 { b2[i] = b1[i] } // good copy(b2, b1) // 新增 // bad var a, b []int for _, v := range a { b = append(b, v) } // good var a, b []int b = append(b, a...)
流程控制
不要在 for 循环里面使用 defer,defer只有在函数退出时才会执行
// bad
for file := range files {
fd, err := os.Open(file)
if err != nil {
return err
}
defer fd.Close()
// normal code
}
// good
for file := range files {
func() {
fd, err := os.Open(file)
if err != nil {
return err
}
defer fd.Close()
// normal code
}()
}
range
for key := range keys {
// normal code
}
sum := 0
for _, value := range array {
sum += value
}
goto不要用
函数
尽量采用值传递,而非指针传递
传入参数是 map、slice、chan、interface ,不要传递指针
如果函数返回相同类型的两个或三个参数,或者如果从上下文中不清楚结果的含义,使用命名返回,其他情况不建议使用命名返回
func coordinate() (x, y float64, err error) { // normal code }
先判断是否错误,再defer释放资源
rep, err := http.Get(url) if err != nil { return err } defer resp.Body.Close()
依赖管理
Go 1.11 以上必须使用 Go Modules。
使用Go Modules作为依赖管理的项目时,不建议提交vendor目录。
使用Go Modules作为依赖管理的项目时,必须提交go.sum文件
最佳实践
尽量少用全局变量,而是通过参数传递
编译验证接口
type LogHandler struct { h http.Handler log *zap.Logger } var _ http.Handler = LogHandler{}
性能
string 表示的是不可变的字符串变量,对 string 的修改是比较重的操作,基本上都需要重新申请内存。所以,如果没有特殊需要,需要修改时多使用 []byte。
优先使用 strconv 而不是 fmt
注意事项
append 要小心自动分配内存,append 返回的可能是新分配的地址。
如果要直接修改 map 的 value 值,则 value 只能是指针,否则要覆盖原来的值。
map 在并发中需要加锁。
编译过程无法检查 interface{} 的转换,只能在运行时检查,小心引起 panic。
最后更新于
这有帮助吗?