diff --git a/docs/conversions/go_code.md b/docs/conversions/go_code.md index 85d099f9c..6e1adf06d 100644 --- a/docs/conversions/go_code.md +++ b/docs/conversions/go_code.md @@ -1,66 +1,62 @@ -## Go 代码开发规范 -在Go 项目开发中,一个好的编码规范可以极大的提高代码质量。为了帮你节省时间和精力,这里我整理了一份清晰、可直接套用的 Go 编码规范,供你参考。 +## OpenIM development specification +We have very high standards for code style and specification, and we want our products to be polished and perfect -这份规范,是我参考了 Go 官方提供的编码规范,以及 Go 社区沉淀的一些比较合理的规范之后,加入自己的理解总结出的,它比很多公司内部的规范更全面,你掌握了,以后在面试大厂的时候,或者在大厂里写代码的时候,都会让人高看你一眼,觉得你code很专业。 +## 1. Code style -这份编码规范中包含代码风格、命名规范、注释规范、类型、控制结构、函数、GOPATH 设置规范、依赖管理和最佳实践九类规范。如果你觉得这些规范内容太多了,看完一遍也记不住,这完全没关系。你可以多看几遍,也可以在用到时把它翻出来,在实际应用中掌握。这篇特别放送的内容,更多是作为写代码时候的一个参考手册。 +### 1.1 Code format -## 1. 代码风格 - -### 1.1 代码格式 - -- 代码都必须用 `gofmt` 进行格式化。 -- 运算符和操作数之间要留空格。 -- 建议一行代码不超过120个字符,超过部分,请采用合适的换行方式换行。但也有些例外场景,例如import行、工具自动生成的代码、带tag的struct字段。 -- 文件长度不能超过800行。 -- 函数长度不能超过80行。 -- import规范 - - 代码都必须用`goimports`进行格式化(建议将代码Go代码编辑器设置为:保存时运行 `goimports`)。 - - 不要使用相对路径引入包,例如 `import ../util/net` 。 - - 包名称与导入路径的最后一个目录名不匹配时,或者多个相同包名冲突时,则必须使用导入别名。 +- Code must be formatted with `gofmt`. +- Leave spaces between operators and operands. +- It is recommended that a line of code does not exceed 120 characters. If the part exceeds, please use an appropriate line break method. But there are also some exception scenarios, such as import lines, code automatically generated by tools, and struct fields with tags. +- The file length cannot exceed 800 lines. +- Function length cannot exceed 80 lines. +- import specification +- All code must be formatted with `goimports` (it is recommended to set the code Go code editor to: run `goimports` on save). +- Do not use relative paths to import packages, such as `import ../util/net`. +- Import aliases must be used when the package name does not match the last directory name of the import path, or when multiple identical package names conflict. ```go - // bad - "github.com/dgrijalva/jwt-go/v4" +// bad +"github.com/dgrijalva/jwt-go/v4" - //good - jwt "github.com/dgrijalva/jwt-go/v4" +//good +jwt "github.com/dgrijalva/jwt-go/v4" ``` - - 导入的包建议进行分组,匿名包的引用使用一个新的分组,并对匿名包引用进行说明。 +- Imported packages are suggested to be grouped, and anonymous package references use a new group, and anonymous package references are explained. ```go - 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" - ) +import ( +// go standard package +"fmt" + +// third party package +"github.com/jinzhu/gorm" +"github.com/spf13/cobra" +"github.com/spf13/viper" + +// Anonymous packages are grouped separately, and anonymous package references are explained +// import mysql driver +_ "github.com/jinzhu/gorm/dialects/mysql" + +// inner package +v1 "github.com/marmotedu/api/apiserver/v1" +metav1 "github.com/marmotedu/apimachinery/pkg/meta/v1" +"github.com/marmotedu/iam/pkg/cli/genericclioptions" +) ``` -### 1.2 声明、初始化和定义 +### 1.2 Declaration, initialization and definition -当函数中需要使用到多个变量时,可以在函数开始处使用`var`声明。在函数外部声明必须使用 `var` ,不要采用 `:=` ,容易踩到变量的作用域的问题。 +When multiple variables need to be used in a function, the `var` declaration can be used at the beginning of the function. Declaration outside the function must use `var`, do not use `:=`, it is easy to step on the scope of the variable. ```go var ( - Width int - Height int +Width int +Height int ) ``` -- 在初始化结构引用时,请使用`&T{}`代替`new(T)`,以使其与结构体初始化一致。 +- When initializing a structure reference, please use `&T{}` instead of `new(T)` to make it consistent with structure initialization. ```go // bad @@ -71,371 +67,371 @@ sptr.Name = "bar" sptr := &T{Name: "bar"} ``` -- struct 声明和初始化格式采用多行,定义如下。 +- The struct declaration and initialization format takes multiple lines and is defined as follows. ```go type User struct{ - Username string - Email string + Username string + Email string } user := User{ - Username: "belm", - Email: "nosbelm@qq.com", +Username: "belm", +Email: "nosbelm@qq.com", } ``` -- 相似的声明放在一组,同样适用于常量、变量和类型声明。 +- Similar declarations are grouped together, and the same applies to constant, variable, and type declarations. ```go // bad import "a" import "b" -// good +//good import ( - "a" - "b" + "a" + "b" ) ``` -- 尽可能指定容器容量,以便为容器预先分配内存,例如: +- Specify container capacity where possible to pre-allocate memory for the container, for example: ```go v := make(map[int]string, 4) v := make([]string, 0, 4) ``` -- 在顶层,使用标准var关键字。请勿指定类型,除非它与表达式的类型不同。 +- At the top level, use the standard var keyword. Do not specify a type unless it is different from the type of the expression. ```go // bad -var _s string = F() +var_s string = F() func F() string { return "A" } // good -var _s = F() -// 由于 F 已经明确了返回一个字符串类型,因此我们没有必要显式指定_s 的类型 -// 还是那种类型 +var_s = F() +// Since F already explicitly returns a string type, we don't need to explicitly specify the type of _s +// still of that type func F() string { return "A" } ``` -- 对于未导出的顶层常量和变量,使用`_`作为前缀。 +- Use `_` as a prefix for unexported top-level constants and variables. ```go // bad const ( - defaultHost = "127.0.0.1" - defaultPort = 8080 + defaultHost = "127.0.0.1" + defaultPort = 8080 ) // good const ( - _defaultHost = "127.0.0.1" - _defaultPort = 8080 + _defaultHost = "127.0.0.1" + _defaultPort = 8080 ) ``` -- 嵌入式类型(例如 mutex)应位于结构体内的字段列表的顶部,并且必须有一个空行将嵌入式字段与常规字段分隔开。 +- Embedded types (such as mutexes) should be at the top of the field list within the struct, and there must be a blank line separating embedded fields from regular fields. ```go // bad type Client struct { - version int - http.Client + version int + http.Client } -// good +//good type Client struct { - http.Client + http.Client - version int + version int } ``` -### 1.3 错误处理 +### 1.3 Error Handling -- `error`作为函数的值返回,必须对`error`进行处理,或将返回值赋值给明确忽略。对于`defer xx.Close()`可以不用显式处理。 +- `error` is returned as the value of the function, `error` must be handled, or the return value assigned to explicitly ignore. For `defer xx.Close()`, there is no need to explicitly handle it. ```go func load() error { - // normal code +// normal code } // bad load() -// good - _ = load() +//good + _ = load() ``` -- `error`作为函数的值返回且有多个返回值的时候,`error`必须是最后一个参数。 +- When `error` is returned as the value of a function and there are multiple return values, `error` must be the last parameter. ```go // bad func load() (error, int) { - // normal code +// normal code } -// good +//good func load() (int, error) { - // normal code +// normal code } ``` -- 尽早进行错误处理,并尽早返回,减少嵌套。 +- Perform error handling as early as possible and return as early as possible to reduce nesting. ```go // bad if err != nil { - // error code +// error code } else { - // normal code +// normal code } -// good +//good if err != nil { - // error handling - return err +// error handling +return err } // normal code ``` -- 如果需要在 if 之外使用函数调用的结果,则应采用下面的方式。 +- If you need to use the result of the function call outside if, you should use the following method. ```go // bad if v, err := foo(); err != nil { - // error handling +// error handling } // good v, err := foo() if err != nil { - // error handling +// error handling } ``` -- 错误要单独判断,不与其他逻辑组合判断。 +- Errors should be judged independently, not combined with other logic. ```go // bad v, err := foo() -if err != nil || v == nil { - // error handling - return err +if err != nil || v == nil { +// error handling +return err } -// good +//good v, err := foo() if err != nil { - // error handling - return err +// error handling +return err } if v == nil { - // error handling - return errors.New("invalid value v") +// error handling +return errors. New("invalid value v") } ``` -- 如果返回值需要初始化,则采用下面的方式。 +- If the return value needs to be initialized, use the following method. ```go v, err := f() if err != nil { - // error handling - return // or continue. + // error handling + return // or continue. } ``` -- 错误描述建议 - - 错误描述用小写字母开头,结尾不要加标点符号,例如: +- Bug description suggestions +- Error descriptions start with a lowercase letter and do not end with punctuation, for example: ```go - // bad - errors.New("Redis connection failed") - errors.New("redis connection failed.") +// bad +errors.New("Redis connection failed") +errors.New("redis connection failed.") - // good - errors.New("redis connection failed") +// good +errors.New("redis connection failed") ``` - - 告诉用户他们可以做什么,而不是告诉他们不能做什么。 - - 当声明一个需求时,用must 而不是should。例如,`must be greater than 0、must match regex '[a-z]+'`。 - - 当声明一个格式不对时,用must not。例如,`must not contain`。 - - 当声明一个动作时用may not。例如,`may not be specified when otherField is empty、only name may be specified`。 - - 引用文字字符串值时,请在单引号中指示文字。例如,`ust not contain '..'`。 - - 当引用另一个字段名称时,请在反引号中指定该名称。例如,must be greater than `request`。 - - 指定不等时,请使用单词而不是符号。例如,`must be less than 256、must be greater than or equal to 0 (不要用 larger than、bigger than、more than、higher than)`。 - - 指定数字范围时,请尽可能使用包含范围。 - - 建议 Go 1.13 以上,error 生成方式为 `fmt.Errorf("module xxx: %w", err)`。 +- Tell users what they can do, not what they can't. +- When declaring a requirement, use must instead of should. For example, `must be greater than 0, must match regex '[a-z]+'`. +- When declaring that a format is incorrect, use must not. For example, `must not contain`. +- Use may not when declaring an action. For example, `may not be specified when otherField is empty, only name may be specified`. +- When quoting a literal string value, indicate the literal in single quotes. For example, `ust not contain '..'`. +- When referencing another field name, specify that name in backticks. For example, must be greater than `request`. +- When specifying unequal, use words instead of symbols. For example, `must be less than 256, must be greater than or equal to 0 (do not use larger than, bigger than, more than, higher than)`. +- When specifying ranges of numbers, use inclusive ranges whenever possible. +- Go 1.13 or above is recommended, and the error generation method is `fmt.Errorf("module xxx: %w", err)`. -### 1.4 panic处理 +### 1.4 panic processing -- 在业务逻辑处理中禁止使用panic。 -- 在main包中,只有当程序完全不可运行时使用panic,例如无法打开文件、无法连接数据库导致程序无法正常运行。 -- 在main包中,使用 `log.Fatal` 来记录错误,这样就可以由log来结束程序,或者将panic抛出的异常记录到日志文件中,方便排查问题。 -- 可导出的接口一定不能有panic。 -- 包内建议采用error而不是panic来传递错误。 +- Panic is prohibited in business logic processing. +- In the main package, panic is only used when the program is completely inoperable, for example, the file cannot be opened, the database cannot be connected, and the program cannot run normally. +- In the main package, use `log.Fatal` to record errors, so that the program can be terminated by the log, or the exception thrown by the panic can be recorded in the log file, which is convenient for troubleshooting. +- An exportable interface must not panic. +- It is recommended to use error instead of panic to convey errors in the package. -### 1.5 单元测试 +### 1.5 Unit Tests -- 单元测试文件名命名规范为 `example_test.go`。 -- 每个重要的可导出函数都要编写测试用例。 -- 因为单元测试文件内的函数都是不对外的,所以可导出的结构体、函数等可以不带注释。 -- 如果存在 `func (b *Bar) Foo` ,单测函数可以为 `func TestBar_Foo`。 +- The unit test filename naming convention is `example_test.go`. +- Write a test case for every important exportable function. +- Because the functions in the unit test file are not external, the exportable structures, functions, etc. can be uncommented. +- If `func (b *Bar) Foo` exists, the single test function can be `func TestBar_Foo`. -### 1.6 类型断言失败处理 +### 1.6 Type assertion failure handling -- type assertion 的单个返回值针对不正确的类型将产生 panic。请始终使用 “comma ok”的惯用法。 +- A single return value from a type assertion will panic for an incorrect type. Always use the "comma ok" idiom. ```go // bad t := n.(int) -// good +//good t, ok := n.(int) if !ok { - // error handling +// error handling } ``` -## 2. 命名规范 +## 2. Naming convention -命名规范是代码规范中非常重要的一部分,一个统一的、短小的、精确的命名规范可以大大提高代码的可读性,也可以借此规避一些不必要的Bug。 +The naming convention is a very important part of the code specification. A uniform, short, and precise naming convention can greatly improve the readability of the code and avoid unnecessary bugs. -### 2.1 包命名 +### 2.1 Package Naming -- 包名必须和目录名一致,尽量采取有意义、简短的包名,不要和标准库冲突。 -- 包名全部小写,没有大写或下划线,使用多级目录来划分层级。 -- 项目名可以通过中划线来连接多个单词。 -- 包名以及包所在的目录名,不要使用复数,例如,是`net/url`,而不是`net/urls`。 -- 不要用 common、util、shared 或者 lib 这类宽泛的、无意义的包名。 -- 包名要简单明了,例如 net、time、log。 +- The package name must be consistent with the directory name, try to use a meaningful and short package name, and do not conflict with the standard library. +- Package names are all lowercase, without uppercase or underscores, and use multi-level directories to divide the hierarchy. +- Item names can connect multiple words with dashes. +- Do not use plurals for the package name and the directory name where the package is located, for example, `net/url` instead of `net/urls`. +- Don't use broad, meaningless package names like common, util, shared or lib. +- The package name should be simple and clear, such as net, time, log. -### 2.2 函数命名 +### 2.2 Function Naming -- 函数名采用驼峰式,首字母根据访问控制决定使用大写或小写,例如:`MixedCaps`或者`mixedCaps`。 -- 代码生成工具自动生成的代码(如`xxxx.pb.go`)和为了对相关测试用例进行分组,而采用的下划线(如`TestMyFunction_WhatIsBeingTested`)排除此规则。 +- The function name is in camel case, and the first letter is uppercase or lowercase according to the access control decision,For example: `MixedCaps` or `mixedCaps`. +- Code automatically generated by code generation tools (such as `xxxx.pb.go`) and underscores used to group related test cases (such as `TestMyFunction_WhatIsBeingTested`) exclude this rule. -### 2.3 文件命名 +### 2.3 File Naming -- 文件名要简短有意义。 -- 文件名应小写,并使用下划线分割单词。 +- Keep the filename short and meaningful. +- Filenames should be lowercase and use underscores to separate words. -### 2.4 结构体命名 +### 2.4 Structure Naming -- 采用驼峰命名方式,首字母根据访问控制决定使用大写或小写,例如`MixedCaps`或者`mixedCaps`。 -- 结构体名不应该是动词,应该是名词,比如 `Node`、`NodeSpec`。 -- 避免使用Data、Info这类无意义的结构体名。 -- 结构体的声明和初始化应采用多行,例如: +- The camel case is adopted, and the first letter is uppercase or lowercase according to the access control, such as `MixedCaps` or `mixedCaps`. +- Struct names should not be verbs, but should be nouns, such as `Node`, `NodeSpec`. +- Avoid using meaningless structure names such as Data and Info. +- The declaration and initialization of the structure should take multiple lines, for example: ```go -// User 多行声明 +// User multi-line declaration type User struct { - Name string - Email string + name string + Email string } -// 多行初始化 +// multi-line initialization u := User{ - UserName: "belm", - Email: "nosbelm@qq.com", + UserName: "belm", + Email: "nosbelm@qq.com", } ``` -### 2.5 接口命名 +### 2.5 Interface Naming -- 接口命名的规则,基本和结构体命名规则保持一致: - - 单个函数的接口名以 “er"”作为后缀(例如Reader,Writer),有时候可能导致蹩脚的英文,但是没关系。 - - 两个函数的接口名以两个函数名命名,例如ReadWriter。 - - 三个以上函数的接口名,类似于结构体名。 +- The interface naming rules are basically consistent with the structure naming rules: +- Interface names of individual functions suffixed with "er"" (e.g. Reader, Writer) can sometimes lead to broken English, but that's okay. +- The interface name of the two functions is named after the two function names, eg ReadWriter. +- An interface name for more than three functions, similar to a structure name. -例如: +For example: ``` - // Seeking to an offset before the start of the file is an error. - // Seeking to any positive offset is legal, but the behavior of subsequent - // I/O operations on the underlying object is implementation-dependent. - type Seeker interface { - Seek(offset int64, whence int) (int64, error) - } +// Seeking to an offset before the start of the file is an error. +// Seeking to any positive offset is legal, but the behavior of subsequent +// I/O operations on the underlying object are implementation-dependent. +type Seeker interface { +Seek(offset int64, whence int) (int64, error) +} - // ReadWriter is the interface that groups the basic Read and Write methods. - type ReadWriter interface { - Reader - Writer - } +// ReadWriter is the interface that groups the basic Read and Write methods. +type ReadWriter interface { +reader +Writer +} ``` -### 2.6 变量命名 +### 2.6 Variable Naming -- 变量名必须遵循驼峰式,首字母根据访问控制决定使用大写或小写。 -- 在相对简单(对象数量少、针对性强)的环境中,可以将一些名称由完整单词简写为单个字母,例如: - - user 可以简写为 u; - - userID 可以简写 uid。 -- 特有名词时,需要遵循以下规则: - - 如果变量为私有,且特有名词为首个单词,则使用小写,如 apiClient。 - - 其他情况都应当使用该名词原有的写法,如 APIClient、repoID、UserID。 +- Variable names must follow camel case, and the initial letter is uppercase or lowercase according to the access control decision. +- In relatively simple (few objects, highly targeted) environments, some names can be abbreviated from full words to single letters, for example: +- user can be abbreviated as u; +- userID can be abbreviated as uid. +- When using proper nouns, the following rules need to be followed: +- If the variable is private and the proper noun is the first word, use lowercase, such as apiClient. +- In other cases, the original wording of the noun should be used, such as APIClient, repoID, UserID. -下面列举了一些常见的特有名词。 +Some common nouns are listed below. ``` // 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开头,例如: - -```go -var hasConflict bool + "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, +} +``` + +- If the variable type is bool, the name should start with Has, Is, Can or Allow, for example: + +```go +var has Conflict bool var isExist bool -var canManage bool +var can Manage bool var allowGitHook bool ``` -- 局部变量应当尽可能短小,比如使用buf指代buffer,使用i指代index。 -- 代码生成工具自动生成的代码可排除此规则(如`xxx.pb.go`里面的Id) +- Local variables should be as short as possible, for example, use buf to refer to buffer, and use i to refer to index. +- The code automatically generated by the code generation tool can exclude this rule (such as the Id in `xxx.pb.go`) -### 2.7 常量命名 +### 2.7 Constant naming -- 常量名必须遵循驼峰式,首字母根据访问控制决定使用大写或小写。 -- 如果是枚举类型的常量,需要先创建相应类型: +- The constant name must follow the camel case, and the initial letter is uppercase or lowercase according to the access control decision. +- If it is a constant of enumeration type, you need to create the corresponding type first: ```go // Code defines an error code type. @@ -443,53 +439,53 @@ type Code int // Internal errors. const ( - // ErrUnknown - 0: An unknown error occurred. - ErrUnknown Code = iota - // ErrFatal - 1: An fatal error occurred. - ErrFatal + // ErrUnknown - 0: An unknown error occurred. + ErrUnknown Code = iota + // ErrFatal - 1: An fatal error occurred. + Err Fatal ) ``` -### 2.8 Error的命名 +### 2.8 Error naming -- Error类型应该写成FooError的形式。 +- The Error type should be written in the form of FooError. ```go type ExitError struct { - // .... +// .... } ``` -- Error变量写成ErrFoo的形式。 +- The Error variable is written in the form of ErrFoo. ```go -var ErrFormat = errors.New("unknown format") +var ErrFormat = errors. New("unknown format") ``` -## 3. 注释规范 +## 3. Comment specification -- 每个可导出的名字都要有注释,该注释对导出的变量、函数、结构体、接口等进行简要介绍。 -- 全部使用单行注释,禁止使用多行注释。 -- 和代码的规范一样,单行注释不要过长,禁止超过 120 字符,超过的请使用换行展示,尽量保持格式优雅。 -- 注释必须是完整的句子,以需要注释的内容作为开头,句点作为结尾,`格式为 // 名称 描述.`。例如: +- Each exportable name must have a comment, which briefly introduces the exported variables, functions, structures, interfaces, etc. +- All single-line comments are used, and multi-line comments are prohibited. +- Same as the code specification, single-line comments should not be too long, and no more than 120 characters are allowed. If it exceeds, please use a new line to display, and try to keep the format elegant. +- A comment must be a complete sentence, starting with the content to be commented and ending with a period, `the format is // name description.`. For example: ```go // bad // logs the flags in the flagset. -func PrintFlags(flags *pflag.FlagSet) { - // normal code +func PrintFlags(flags *pflag. FlagSet) { +// normal code } -// good +//good // PrintFlags logs the flags in the flagset. -func PrintFlags(flags *pflag.FlagSet) { - // normal code +func PrintFlags(flags *pflag. FlagSet) { +// normal code } ``` -- 所有注释掉的代码在提交code review前都应该被删除,否则应该说明为什么不删除,并给出后续处理建议。 +- All commented out code should be deleted before submitting code review, otherwise, it should explain why it is not deleted, and give follow-up processing suggestions. -- 在多段注释之间可以使用空行分隔加以区分,如下所示: +- Multiple comments can be separated by blank lines, as follows: ```go // Package superman implements methods for saving the world. @@ -499,10 +495,10 @@ func PrintFlags(flags *pflag.FlagSet) { package superman ``` -### 3.1 包注释 +### 3.1 Package Notes -- 每个包都有且仅有一个包级别的注释。 -- 包注释统一用 // 进行注释,格式为 `// Package 包名 包描述`,例如: +- Each package has one and only one package-level annotation. +- Package comments are uniformly commented with // in the format of `// Package package name package description`, for example: ```go // Package genericclioptions contains flags which can be added to you command, bound, completed, and produce @@ -510,90 +506,90 @@ package superman package genericclioptions ``` -### 3.2 变量/常量注释 +### 3.2 Variable/Constant Comments -- 每个可导出的变量/常量都必须有注释说明,`格式为// 变量名 变量描述`,例如: +- Each variable/constant that can be exported must have a comment description, `the format is // variable name variable description`, for example: ```go // ErrSigningMethod defines invalid signing method error. -var ErrSigningMethod = errors.New("Invalid signing method") +var ErrSigningMethod = errors. New("Invalid signing method") ``` -- 出现大块常量或变量定义时,可在前面注释一个总的说明,然后在每一行常量的前一行或末尾详细注释该常量的定义,例如: +- When there is a large block of constant or variable definition, you can comment a general description in front, and then comment the definition of the constant in detail before or at the end of each line of constant, for example: ```go // Code must start with 1xxxxx. const ( - // ErrSuccess - 200: OK. - ErrSuccess int = iota + 100001 + // ErrSuccess - 200: OK. + ErrSuccess int = iota + 100001 - // ErrUnknown - 500: Internal server error. - ErrUnknown + // ErrUnknown - 500: Internal server error. + ErrUnknown - // ErrBind - 400: Error occurred while binding the request body to the struct. - ErrBind + // ErrBind - 400: Error occurred while binding the request body to the struct. + ErrBind - // ErrValidation - 400: Validation failed. - ErrValidation + // ErrValidation - 400: Validation failed. + ErrValidation ) ``` -### 3.3 结构体注释 +### 3.3 Structure Annotation -- 每个需要导出的结构体或者接口都必须有注释说明,格式为 `// 结构体名 结构体描述.`。 -- 结构体内的可导出成员变量名,如果意义不明确,必须要给出注释,放在成员变量的前一行或同一行的末尾。例如: +- Each structure or interface that needs to be exported must have a comment description, the format is `// structure name structure description.`. +- The name of the exportable member variable in the structure, if the meaning is not clear, a comment must be given and placed before the member variable or at the end of the same line. For example: ```go // User represents a user restful resource. It is also used as gorm model. type User struct { - // Standard object's metadata. - metav1.ObjectMeta `json:"metadata,omitempty"` + // Standard object's metadata. + metav1.ObjectMeta `json:"metadata,omitempty"` - Nickname string `json:"nickname" gorm:"column:nickname"` - Password string `json:"password" gorm:"column:password"` - Email string `json:"email" gorm:"column:email"` - Phone string `json:"phone" gorm:"column:phone"` - IsAdmin int `json:"isAdmin,omitempty" gorm:"column:isAdmin"` + Nickname string `json:"nickname" gorm:"column:nickname"` + Password string `json:"password" gorm:"column:password"` + Email string `json:"email" gorm:"column:email"` + Phone string `json:"phone" gorm:"column:phone"` + IsAdmin int `json:"isAdmin,omitempty" gorm:"column:isAdmin"` } ``` -### 3.4 方法注释 +### 3.4 Method Notes -每个需要导出的函数或者方法都必须有注释,格式为// 函数名 函数描述.,例如: +Each function or method that needs to be exported must have a comment, the format is // function name function description., for examplelike: ```go // BeforeUpdate run before update database record. func (p *Policy) BeforeUpdate() (err error) { - // normal code - return nil +// normal code +return nil } ``` -### 3.5 类型注释 +### 3.5 Type annotations -- 每个需要导出的类型定义和类型别名都必须有注释说明,格式为 `// 类型名 类型描述.`,例如: +- Each type definition and type alias that needs to be exported must have a comment description, the format is `// type name type description.`, for example: ```go // Code defines an error code type. type Code int ``` -## 4. 类型 +## 4. Type -### 4.1 字符串 +### 4.1 Strings -- 空字符串判断。 +- Empty string judgment. ```go // bad if s == "" { - // normal code + // normal code } -// good +//good if len(s) == 0 { - // normal code + // normal code } ``` -- `[]byte`/`string`相等比较。 +- `[]byte`/`string` equality comparison. ```go // bad @@ -603,165 +599,165 @@ var s2 []byte bytes.Equal(s1, s2) == 0 bytes.Equal(s1, s2) != 0 -// good +//good var s1 []byte var s2 []byte ... -bytes.Compare(s1, s2) == 0 -bytes.Compare(s1, s2) != 0 +bytes. Compare(s1, s2) == 0 +bytes. Compare(s1, s2) != 0 ``` -- 复杂字符串使用raw字符串避免字符转义。 +- Complex strings use raw strings to avoid character escaping. ```go // bad regexp.MustCompile("\\.") -// good +//good regexp.MustCompile(`\.`) ``` -### 4.2 切片 +### 4.2 Slicing -- 空slice判断。 +- Empty slice judgment. ```go // bad if len(slice) = 0 { - // normal code + // normal code } -// good +//good if slice != nil && len(slice) == 0 { - // normal code + // normal code } ``` -上面判断同样适用于map、channel。 +The above judgment also applies to map and channel. -- 声明slice。 +- Declare a slice. ```go // bad s := []string{} s := make([]string, 0) -// good -var s []string +//good +var s[]string ``` -- slice复制。 +- slice copy. ```go // bad var b1, b2 []byte for i, v := range b1 { - b2[i] = v + b2[i] = v } for i := range b1 { - b2[i] = b1[i] + b2[i] = b1[i] } -// good +//good copy(b2, b1) ``` -- slice新增。 +- slice added. ```go // bad var a, b []int for _, v := range a { - b = append(b, v) + b = append(b, v) } -// good +//good var a, b []int b = append(b, a...) ``` -### 4.3 结构体 +### 4.3 Structure -- struct初始化。 +- struct initialization. -struct以多行格式初始化。 +The struct is initialized in multi-line format. ```go type user struct { - Id int64 - Name string +Id int64 +name string } u1 := user{100, "Colin"} u2 := user{ - Id: 200, - Name: "Lex", + Id: 200, + Name: "Lex", } ``` -## 5. 控制结构 +## 5. Control Structure ### 5.1 if -- if 接受初始化语句,约定如下方式建立局部变量。 +- if accepts the initialization statement, the convention is to create local variables in the following way. ```go if err := loadConfig(); err != nil { - // error handling - return err +// error handling +return err } ``` -- if 对于bool类型的变量,应直接进行真假判断。 +- if For variables of bool type, true and false judgments should be made directly. ```go var isAllow bool if isAllow { - // normal code +// normal code } ``` ### 5.2 for -- 采用短声明建立局部变量。 +- Create local variables using short declarations. ```go sum := 0 for i := 0; i < 10; i++ { - sum += 1 + sum += 1 } ``` -- 不要在 for 循环里面使用 defer,defer只有在函数退出时才会执行。 +- Don't use defer in for loop, defer will only be executed when the function exits. ```go // bad for file := range files { - fd, err := os.Open(file) - if err != nil { - return err - } - defer fd.Close() - // normal code +fd, err := os. Open(file) +if err != nil { +return err +} +defer fd. Close() +// normal code } -// good +//good for file := range files { - func() { - fd, err := os.Open(file) - if err != nil { - return err - } - defer fd.Close() - // normal code - }() +func() { +fd, err := os. Open(file) +if err != nil { +return err +} +defer fd. Close() +// normal code +}() } ``` ### 5.3 range -- 如果只需要第一项(key),就丢弃第二个。 +- If only the first item (key) is needed, discard the second. ```go for key := range keys { @@ -769,133 +765,129 @@ for key := range keys { } ``` -- 如果只需要第二项,则把第一项置为下划线。 +- If only the second item is required, underline the first item. ```go sum := 0 for _, value := range array { - sum += value + sum += value } ``` ### 5.4 switch -- 必须要有default。 +- must have default. ```go switch os := runtime.GOOS; os { - case "linux": - fmt.Println("Linux.") - case "darwin": - fmt.Println("OS X.") - default: - fmt.Printf("%s.\n", os) + case "linux": + fmt.Println("Linux.") + case "darwin": + fmt.Println("OS X.") + default: + fmt.Printf("%s.\n", os) } ``` ### 5.5 goto -- 业务代码禁止使用 goto 。 -- 框架或其他底层源码尽量不用。 +- Business code prohibits the use of goto. +- Try not to use frameworks or other low-level source code. -## 6. 函数 +## 6. Functions -- 传入变量和返回变量以小写字母开头。 -- 函数参数个数不能超过5个。 -- 函数分组与顺序 -- 函数应按粗略的调用顺序排序。 -- 同一文件中的函数应按接收者分组。 -- 尽量采用值传递,而非指针传递。 -- 传入参数是 map、slice、chan、interface ,不要传递指针。 +- Incoming variables and return variables start with a lowercase letter. +- The number of function parameters cannot exceed 5. +- Function grouping and ordering +- Functions should be sorted in rough calling order. +- Functions in the same file should be grouped by receiver. +- Try to use value transfer instead of pointer transfer. +- The incoming parameters are map, slice, chan, interface, do not pass pointers. -### 6.1 函数参数 +### 6.1 Function parameters -- 如果函数返回相同类型的两个或三个参数,或者如果从上下文中不清楚结果的含义,使用命名返回,其他情况不建议使用命名返回,例如: +- If the function returns two or three arguments of the same type, or if the meaning of the result is not clear from the context, use named returns, otherwise it is not recommended to use named returns, for example: ```go func coordinate() (x, y float64, err error) { - // normal code +// normal code } ``` -- 传入变量和返回变量都以小写字母开头。 -- 尽量用值传递,非指针传递。 -- 参数数量均不能超过5个。 -- 多返回值最多返回三个,超过三个请使用 struct。 +- Both incoming and returned variables start with a lowercase letter. +- Try to pass by value instead of pointer. +- The number of parameters cannot exceed 5. +- Multiple return values can return up to three, and if there are more than three, please use struct. ### 6.2 defer -- 当存在资源创建时,应紧跟defer释放资源(可以大胆使用defer,defer在Go1.14版本中,性能大幅提升,defer的性能损耗即使在性能敏感型的业务中,也可以忽略)。 -- 先判断是否错误,再defer释放资源,例如: +- When resources are created, resources should be released immediately after defer (defer can be used boldly, the performance of defer is greatly improved in Go1.14 version, and the performance loss of defer can be ignored even in performance-sensitive businesses). +- First judge whether there is an error, and then defer to release resources, for example: ```go -rep, err := http.Get(url) +rep, err := http. Get(url) if err != nil { - return err + return err } defer resp.Body.Close() ``` -### 6.3 方法的接收器 +### 6.3 Method Receiver -- 推荐以类名第一个英文首字母的小写作为接收器的命名。 -- 接收器的命名在函数超过20行的时候不要用单字符。 -- 接收器的命名不能采用me、this、self这类易混淆名称。 +- It is recommended to use the lowercase of the first English letter of the class name as the name of the receiver. +- Don't use a single character in the name of the receiver when the function exceeds 20 lines. +- The name of the receiver cannot use confusing names such as me, this, and self. -### 6.4 嵌套 -- 嵌套深度不能超过4层。 +### 6.4 Nesting +- The nesting depth cannot exceed 4 levels. -### 6.5 变量命名 -- 变量声明尽量放在变量第一次使用的前面,遵循就近原则。 -- 如果魔法数字出现超过两次,则禁止使用,改用一个常量代替,例如: +### 6.5 Variable Naming +- The variable declaration should be placed before the first use of the variable as far as possible, following the principle of proximity. +- If the magic number appears more than twice, it is forbidden to use it and use a constant instead, for example: ```go -// PI ... -const Prise = 3.14 +// PI... +const Price = 3.14 func getAppleCost(n float64) float64 { - return Prise * n +return Price * n } func getOrangeCost(n float64) float64 { - return Prise * n +return Price * n } ``` -## 7. GOPATH 设置规范 -- Go 1.11 之后,弱化了 GOPATH 规则,已有代码(很多库肯定是在1.11之前建立的)肯定符合这个规则,建议保留 GOPATH 规则,便于维护代码。 -- 建议只使用一个 GOPATH,不建议使用多个 GOPATH。如果使用多个GOPATH,编译生效的 bin 目录是在第一个 GOPATH 下。 +## 7. GOPATH setting specification +- After Go 1.11, the GOPATH rule has been weakened. Existing code (many libraries must have been created before 1.11) must conform to this rule. It is recommended to keep the GOPATH rule to facilitate code maintenance. +- Only one GOPATH is recommended, multiple GOPATHs are not recommended. If multiple GOPATHs are used, the bin directory where compilation takes effect is under the first GOPATH. -## 8. 依赖管理 +## 8. Dependency Management -- Go 1.11 以上必须使用 Go Modules。 -- 使用Go Modules作为依赖管理的项目时,不建议提交vendor目录。 -- 使用Go Modules作为依赖管理的项目时,必须提交go.sum文件。 +- Go 1.11 and above must use Go Modules. +- When using Go Modules as a dependency management project, it is not recommended to submit the vendor directory. +- When using Go Modules as a dependency management project, the go.sum file must be submitted. -### 9. 最佳实践 +### 9. Best Practices -- 尽量少用全局变量,而是通过参数传递,使每个函数都是“无状态”的。这样可以减少耦合,也方便分工和单元测试。 -- 在编译时验证接口的符合性,例如: +- Minimize the use of global variables, but pass parameters, so that each function is "stateless". This reduces coupling and facilitates division of labor and unit testing. +- Verify interface compliance at compile time, for example: ```go type LogHandler struct { - h http.Handler - log *zap.Logger + h http.Handler + log *zap. Logger } -var _ http.Handler = LogHandler{} +var_http.Handler = LogHandler{} ``` -- 服务器处理请求时,应该创建一个context,保存该请求的相关信息(如requestID),并在函数调用链中传递。 - -### 9.1 性能 -- string 表示的是不可变的字符串变量,对 string 的修改是比较重的操作,基本上都需要重新申请内存。所以,如果没有特殊需要,需要修改时多使用 []byte。 -- 优先使用 strconv 而不是 fmt。 - -### 9.2 注意事项 +- When the server processes a request, it should create a context, save the relevant information of the request (such as requestID), and pass it in the function call chain. -- append 要小心自动分配内存,append 返回的可能是新分配的地址。 -- 如果要直接修改 map 的 value 值,则 value 只能是指针,否则要覆盖原来的值。 -- map 在并发中需要加锁。 -- 编译过程无法检查 interface{} 的转换,只能在运行时检查,小心引起 panic。 +### 9.1 Performance +- string represents an immutable string variable, modifying string is a relatively heavy operation, and basically needs to re-apply for memory. Therefore, if there is no special need, use []byte more when you need to modify. +- Prefer strconv over fmt. -## 总结 +### 9.2 Precautions -这里向你介绍了九类常用的编码规范。但今天的最后,我要在这里提醒你一句:规范是人定的,你也可以根据需要,制定符合你项目的规范,但同时我也建议你采纳这些业界沉淀下来的规范,并通过工具来确保规范的执行。 +- append Be careful about automatically allocating memory, append may return a newly allocated address. +- If you want to directly modify the value of the map, the value can only be a pointer, otherwise the original value must be overwritten. +- map needs to be locked during concurrency. +- The conversion of interface{} cannot be checked during compilation, it can only be checked at runtime, be careful to cause panic.