You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
903 lines
24 KiB
903 lines
24 KiB
1 year ago
|
## OpenIM development specification
|
||
|
We have very high standards for code style and specification, and we want our products to be polished and perfect
|
||
1 year ago
|
|
||
1 year ago
|
## 1. Code style
|
||
1 year ago
|
|
||
1 year ago
|
### 1.1 Code format
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
|
```go
|
||
1 year ago
|
// bad
|
||
|
"github.com/dgrijalva/jwt-go/v4"
|
||
1 year ago
|
|
||
1 year ago
|
//good
|
||
|
jwt "github.com/dgrijalva/jwt-go/v4"
|
||
1 year ago
|
```
|
||
1 year ago
|
- Imported packages are suggested to be grouped, and anonymous package references use a new group, and anonymous package references are explained.
|
||
1 year ago
|
|
||
|
```go
|
||
1 year ago
|
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
|
||
|
)
|
||
1 year ago
|
```
|
||
|
|
||
1 year ago
|
### 1.2 Declaration, initialization and definition
|
||
1 year ago
|
|
||
1 year ago
|
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.
|
||
1 year ago
|
|
||
|
```go
|
||
|
var (
|
||
1 year ago
|
Width int
|
||
|
Height int
|
||
1 year ago
|
)
|
||
|
```
|
||
|
|
||
1 year ago
|
- When initializing a structure reference, please use `&T{}` instead of `new(T)` to make it consistent with structure initialization.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
sptr := new(T)
|
||
|
sptr.Name = "bar"
|
||
|
|
||
|
// good
|
||
|
sptr := &T{Name: "bar"}
|
||
|
```
|
||
|
|
||
1 year ago
|
- The struct declaration and initialization format takes multiple lines and is defined as follows.
|
||
1 year ago
|
|
||
|
```go
|
||
|
type User struct{
|
||
1 year ago
|
Username string
|
||
|
Email string
|
||
1 year ago
|
}
|
||
|
|
||
|
user := User{
|
||
1 year ago
|
Username: "belm",
|
||
|
Email: "nosbelm@qq.com",
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- Similar declarations are grouped together, and the same applies to constant, variable, and type declarations.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
import "a"
|
||
|
import "b"
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
import (
|
||
1 year ago
|
"a"
|
||
|
"b"
|
||
1 year ago
|
)
|
||
|
```
|
||
|
|
||
1 year ago
|
- Specify container capacity where possible to pre-allocate memory for the container, for example:
|
||
1 year ago
|
|
||
|
```go
|
||
|
v := make(map[int]string, 4)
|
||
|
v := make([]string, 0, 4)
|
||
|
```
|
||
|
|
||
1 year ago
|
- At the top level, use the standard var keyword. Do not specify a type unless it is different from the type of the expression.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
12 months ago
|
var s string = F()
|
||
1 year ago
|
|
||
|
func F() string { return "A" }
|
||
|
|
||
|
// good
|
||
12 months ago
|
var s = F()
|
||
1 year ago
|
// Since F already explicitly returns a string type, we don't need to explicitly specify the type of _s
|
||
|
// still of that type
|
||
1 year ago
|
|
||
|
func F() string { return "A" }
|
||
|
```
|
||
|
|
||
1 year ago
|
- Use `_` as a prefix for unexported top-level constants and variables.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
const (
|
||
1 year ago
|
defaultHost = "127.0.0.1"
|
||
|
defaultPort = 8080
|
||
1 year ago
|
)
|
||
|
|
||
|
// good
|
||
|
const (
|
||
1 year ago
|
_defaultHost = "127.0.0.1"
|
||
|
_defaultPort = 8080
|
||
1 year ago
|
)
|
||
|
```
|
||
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
type Client struct {
|
||
1 year ago
|
version int
|
||
|
http.Client
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
type Client struct {
|
||
1 year ago
|
http.Client
|
||
1 year ago
|
|
||
1 year ago
|
version int
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
### 1.3 Error Handling
|
||
1 year ago
|
|
||
1 year ago
|
- `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.
|
||
1 year ago
|
|
||
|
```go
|
||
|
func load() error {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
|
||
|
// bad
|
||
|
load()
|
||
|
|
||
1 year ago
|
//good
|
||
|
_ = load()
|
||
1 year ago
|
```
|
||
|
|
||
1 year ago
|
- When `error` is returned as the value of a function and there are multiple return values, `error` must be the last parameter.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
func load() (error, int) {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
func load() (int, error) {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- Perform error handling as early as possible and return as early as possible to reduce nesting.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
if err != nil {
|
||
1 year ago
|
// error code
|
||
1 year ago
|
} else {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
if err != nil {
|
||
1 year ago
|
// error handling
|
||
|
return err
|
||
1 year ago
|
}
|
||
|
// normal code
|
||
|
```
|
||
|
|
||
1 year ago
|
- If you need to use the result of the function call outside if, you should use the following method.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
if v, err := foo(); err != nil {
|
||
1 year ago
|
// error handling
|
||
1 year ago
|
}
|
||
|
|
||
|
// good
|
||
|
v, err := foo()
|
||
|
if err != nil {
|
||
1 year ago
|
// error handling
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- Errors should be judged independently, not combined with other logic.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
v, err := foo()
|
||
1 year ago
|
if err != nil || v == nil {
|
||
|
// error handling
|
||
|
return err
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
v, err := foo()
|
||
|
if err != nil {
|
||
1 year ago
|
// error handling
|
||
|
return err
|
||
1 year ago
|
}
|
||
|
|
||
|
if v == nil {
|
||
1 year ago
|
// error handling
|
||
|
return errors. New("invalid value v")
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- If the return value needs to be initialized, use the following method.
|
||
1 year ago
|
|
||
|
```go
|
||
|
v, err := f()
|
||
|
if err != nil {
|
||
1 year ago
|
// error handling
|
||
|
return // or continue.
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- Bug description suggestions
|
||
|
- Error descriptions start with a lowercase letter and do not end with punctuation, for example:
|
||
1 year ago
|
```go
|
||
1 year ago
|
// bad
|
||
|
errors.New("Redis connection failed")
|
||
|
errors.New("redis connection failed.")
|
||
1 year ago
|
|
||
1 year ago
|
// good
|
||
|
errors.New("redis connection failed")
|
||
1 year ago
|
```
|
||
1 year ago
|
- 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 year ago
|
|
||
1 year ago
|
### 1.4 panic processing
|
||
1 year ago
|
|
||
1 year ago
|
- 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 year ago
|
|
||
1 year ago
|
### 1.5 Unit Tests
|
||
1 year ago
|
|
||
1 year ago
|
- 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 year ago
|
|
||
1 year ago
|
### 1.6 Type assertion failure handling
|
||
1 year ago
|
|
||
1 year ago
|
- A single return value from a type assertion will panic for an incorrect type. Always use the "comma ok" idiom.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
t := n.(int)
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
t, ok := n.(int)
|
||
|
if !ok {
|
||
1 year ago
|
// error handling
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
## 2. Naming convention
|
||
1 year ago
|
|
||
1 year ago
|
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.
|
||
1 year ago
|
|
||
1 year ago
|
### 2.1 Package Naming
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
1 year ago
|
### 2.2 Function Naming
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
10 months ago
|
In accordance with the naming conventions adopted by OpenIM and drawing reference from the Google Naming Conventions as per the guidelines available at https://google.github.io/styleguide/go/, the following expectations for naming practices within the project are set forth:
|
||
|
|
||
|
1. **File Names:**
|
||
|
+ Both hyphens (`-`) and underscores (`_`) are permitted when naming files.
|
||
|
+ A preference is established for the use of underscores (`_`), suggesting it as the best practice in general scenarios.
|
||
|
2. **Script and Markdown Files:**
|
||
|
+ For shell scripts (bash files) and Markdown (`.md`) documents, the use of hyphens (`-`) is recommended.
|
||
|
+ This recommendation is based on the improved searchability and compatibility in web browsers when hyphens are used in names.
|
||
|
3. **Directories:**
|
||
|
+ A consistent approach is mandated for naming directories, exclusively using hyphens (`-`) to separate words within directory names.
|
||
|
|
||
|
|
||
1 year ago
|
### 2.3 File Naming
|
||
1 year ago
|
|
||
1 year ago
|
- Keep the filename short and meaningful.
|
||
|
- Filenames should be lowercase and use underscores to separate words.
|
||
1 year ago
|
|
||
1 year ago
|
### 2.4 Structure Naming
|
||
1 year ago
|
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
|
||
|
```go
|
||
1 year ago
|
// User multi-line declaration
|
||
1 year ago
|
type User struct {
|
||
1 year ago
|
name string
|
||
|
Email string
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
// multi-line initialization
|
||
1 year ago
|
u := User{
|
||
1 year ago
|
UserName: "belm",
|
||
|
Email: "nosbelm@qq.com",
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
### 2.5 Interface Naming
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
1 year ago
|
For example:
|
||
1 year ago
|
|
||
|
```
|
||
1 year ago
|
// 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)
|
||
|
}
|
||
1 year ago
|
|
||
1 year ago
|
// ReadWriter is the interface that groups the basic Read and Write methods.
|
||
|
type ReadWriter interface {
|
||
|
reader
|
||
|
Writer
|
||
|
}
|
||
1 year ago
|
```
|
||
|
|
||
1 year ago
|
### 2.6 Variable Naming
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
1 year ago
|
Some common nouns are listed below.
|
||
1 year ago
|
|
||
|
```
|
||
|
// A GonicMapper that contains a list of common initialisms taken from golang/lint
|
||
|
var LintGonicMapper = GonicMapper{
|
||
1 year ago
|
"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
|
||
1 year ago
|
var isExist bool
|
||
1 year ago
|
var can Manage bool
|
||
1 year ago
|
var allowGitHook bool
|
||
|
```
|
||
|
|
||
1 year ago
|
- 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`)
|
||
1 year ago
|
|
||
1 year ago
|
### 2.7 Constant naming
|
||
1 year ago
|
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
|
||
|
```go
|
||
|
// Code defines an error code type.
|
||
|
type Code int
|
||
|
|
||
|
// Internal errors.
|
||
|
const (
|
||
1 year ago
|
// ErrUnknown - 0: An unknown error occurred.
|
||
|
ErrUnknown Code = iota
|
||
|
// ErrFatal - 1: An fatal error occurred.
|
||
12 months ago
|
ErrFatal
|
||
1 year ago
|
)
|
||
|
```
|
||
|
|
||
1 year ago
|
### 2.8 Error naming
|
||
1 year ago
|
|
||
1 year ago
|
- The Error type should be written in the form of FooError.
|
||
1 year ago
|
|
||
|
```go
|
||
|
type ExitError struct {
|
||
1 year ago
|
// ....
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- The Error variable is written in the form of ErrFoo.
|
||
1 year ago
|
|
||
|
```go
|
||
1 year ago
|
var ErrFormat = errors. New("unknown format")
|
||
1 year ago
|
```
|
||
|
|
||
1 year ago
|
## 3. Comment specification
|
||
1 year ago
|
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
// logs the flags in the flagset.
|
||
1 year ago
|
func PrintFlags(flags *pflag. FlagSet) {
|
||
|
// normal code
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
// PrintFlags logs the flags in the flagset.
|
||
1 year ago
|
func PrintFlags(flags *pflag. FlagSet) {
|
||
|
// normal code
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
1 year ago
|
- Multiple comments can be separated by blank lines, as follows:
|
||
1 year ago
|
|
||
|
```go
|
||
|
// Package superman implements methods for saving the world.
|
||
|
//
|
||
|
// Experience has shown that a small number of procedures can prove
|
||
|
// helpful when attempting to save the world.
|
||
|
package superman
|
||
|
```
|
||
|
|
||
1 year ago
|
### 3.1 Package Notes
|
||
1 year ago
|
|
||
1 year ago
|
- Each package has one and only one package-level annotation.
|
||
12 months ago
|
- Package comments are uniformly commented with // in the format of `// Package <package name> package description`, for example:
|
||
1 year ago
|
|
||
|
```go
|
||
|
// Package genericclioptions contains flags which can be added to you command, bound, completed, and produce
|
||
|
// useful helper functions.
|
||
|
package genericclioptions
|
||
|
```
|
||
|
|
||
1 year ago
|
### 3.2 Variable/Constant Comments
|
||
1 year ago
|
|
||
1 year ago
|
- Each variable/constant that can be exported must have a comment description, `the format is // variable name variable description`, for example:
|
||
1 year ago
|
|
||
|
```go
|
||
|
// ErrSigningMethod defines invalid signing method error.
|
||
1 year ago
|
var ErrSigningMethod = errors. New("Invalid signing method")
|
||
1 year ago
|
```
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
```go
|
||
|
// Code must start with 1xxxxx.
|
||
|
const (
|
||
1 year ago
|
// ErrSuccess - 200: OK.
|
||
|
ErrSuccess int = iota + 100001
|
||
1 year ago
|
|
||
1 year ago
|
// ErrUnknown - 500: Internal server error.
|
||
|
ErrUnknown
|
||
1 year ago
|
|
||
1 year ago
|
// ErrBind - 400: Error occurred while binding the request body to the struct.
|
||
|
ErrBind
|
||
1 year ago
|
|
||
1 year ago
|
// ErrValidation - 400: Validation failed.
|
||
|
ErrValidation
|
||
1 year ago
|
)
|
||
|
```
|
||
1 year ago
|
### 3.3 Structure Annotation
|
||
1 year ago
|
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
|
||
|
```go
|
||
|
// User represents a user restful resource. It is also used as gorm model.
|
||
|
type User struct {
|
||
1 year ago
|
// Standard object's metadata.
|
||
|
metav1.ObjectMeta `json:"metadata,omitempty"`
|
||
1 year ago
|
|
||
1 year ago
|
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"`
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
### 3.4 Method Notes
|
||
1 year ago
|
|
||
1 year ago
|
Each function or method that needs to be exported must have a comment, the format is // function name function description., for examplelike:
|
||
1 year ago
|
|
||
|
```go
|
||
|
// BeforeUpdate run before update database record.
|
||
|
func (p *Policy) BeforeUpdate() (err error) {
|
||
1 year ago
|
// normal code
|
||
|
return nil
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
### 3.5 Type annotations
|
||
1 year ago
|
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
|
||
|
```go
|
||
|
// Code defines an error code type.
|
||
|
type Code int
|
||
|
```
|
||
|
|
||
1 year ago
|
## 4. Type
|
||
1 year ago
|
|
||
1 year ago
|
### 4.1 Strings
|
||
1 year ago
|
|
||
1 year ago
|
- Empty string judgment.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
if s == "" {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
if len(s) == 0 {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- `[]byte`/`string` equality comparison.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
var s1 []byte
|
||
|
var s2 []byte
|
||
|
...
|
||
|
bytes.Equal(s1, s2) == 0
|
||
|
bytes.Equal(s1, s2) != 0
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
var s1 []byte
|
||
|
var s2 []byte
|
||
|
...
|
||
1 year ago
|
bytes. Compare(s1, s2) == 0
|
||
|
bytes. Compare(s1, s2) != 0
|
||
1 year ago
|
```
|
||
|
|
||
1 year ago
|
- Complex strings use raw strings to avoid character escaping.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
regexp.MustCompile("\\.")
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
regexp.MustCompile(`\.`)
|
||
|
```
|
||
|
|
||
1 year ago
|
### 4.2 Slicing
|
||
1 year ago
|
|
||
1 year ago
|
- Empty slice judgment.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
if len(slice) = 0 {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
if slice != nil && len(slice) == 0 {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
The above judgment also applies to map and channel.
|
||
1 year ago
|
|
||
1 year ago
|
- Declare a slice.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
s := []string{}
|
||
|
s := make([]string, 0)
|
||
|
|
||
1 year ago
|
//good
|
||
|
var s[]string
|
||
1 year ago
|
```
|
||
|
|
||
1 year ago
|
- slice copy.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
var b1, b2 []byte
|
||
|
for i, v := range b1 {
|
||
1 year ago
|
b2[i] = v
|
||
1 year ago
|
}
|
||
|
for i := range b1 {
|
||
1 year ago
|
b2[i] = b1[i]
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
copy(b2, b1)
|
||
|
```
|
||
|
|
||
1 year ago
|
- slice added.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
var a, b []int
|
||
|
for _, v := range a {
|
||
1 year ago
|
b = append(b, v)
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
var a, b []int
|
||
|
b = append(b, a...)
|
||
|
```
|
||
|
|
||
1 year ago
|
### 4.3 Structure
|
||
1 year ago
|
|
||
1 year ago
|
- struct initialization.
|
||
1 year ago
|
|
||
1 year ago
|
The struct is initialized in multi-line format.
|
||
1 year ago
|
|
||
|
```go
|
||
|
type user struct {
|
||
1 year ago
|
Id int64
|
||
|
name string
|
||
1 year ago
|
}
|
||
|
|
||
|
u1 := user{100, "Colin"}
|
||
|
|
||
|
u2 := user{
|
||
1 year ago
|
Id: 200,
|
||
|
Name: "Lex",
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
## 5. Control Structure
|
||
1 year ago
|
|
||
|
### 5.1 if
|
||
|
|
||
1 year ago
|
- if accepts the initialization statement, the convention is to create local variables in the following way.
|
||
1 year ago
|
|
||
|
```go
|
||
|
if err := loadConfig(); err != nil {
|
||
1 year ago
|
// error handling
|
||
|
return err
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- if For variables of bool type, true and false judgments should be made directly.
|
||
1 year ago
|
|
||
|
```go
|
||
|
var isAllow bool
|
||
|
if isAllow {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
|
### 5.2 for
|
||
|
|
||
1 year ago
|
- Create local variables using short declarations.
|
||
1 year ago
|
|
||
|
```go
|
||
|
sum := 0
|
||
|
for i := 0; i < 10; i++ {
|
||
1 year ago
|
sum += 1
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- Don't use defer in for loop, defer will only be executed when the function exits.
|
||
1 year ago
|
|
||
|
```go
|
||
|
// bad
|
||
|
for file := range files {
|
||
1 year ago
|
fd, err := os. Open(file)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer fd. Close()
|
||
|
// normal code
|
||
1 year ago
|
}
|
||
|
|
||
1 year ago
|
//good
|
||
1 year ago
|
for file := range files {
|
||
1 year ago
|
func() {
|
||
|
fd, err := os. Open(file)
|
||
|
if err != nil {
|
||
|
return err
|
||
|
}
|
||
|
defer fd. Close()
|
||
|
// normal code
|
||
|
}()
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
|
### 5.3 range
|
||
|
|
||
1 year ago
|
- If only the first item (key) is needed, discard the second.
|
||
1 year ago
|
|
||
|
```go
|
||
12 months ago
|
for keyIndex := range keys {
|
||
1 year ago
|
// normal code
|
||
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
- If only the second item is required, underline the first item.
|
||
1 year ago
|
|
||
|
```go
|
||
|
sum := 0
|
||
|
for _, value := range array {
|
||
1 year ago
|
sum += value
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
|
### 5.4 switch
|
||
|
|
||
1 year ago
|
- must have default.
|
||
1 year ago
|
|
||
|
```go
|
||
|
switch os := runtime.GOOS; os {
|
||
1 year ago
|
case "linux":
|
||
|
fmt.Println("Linux.")
|
||
|
case "darwin":
|
||
|
fmt.Println("OS X.")
|
||
|
default:
|
||
|
fmt.Printf("%s.\n", os)
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
|
### 5.5 goto
|
||
1 year ago
|
- Business code prohibits the use of goto.
|
||
|
- Try not to use frameworks or other low-level source code.
|
||
1 year ago
|
|
||
1 year ago
|
## 6. Functions
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
1 year ago
|
### 6.1 Function parameters
|
||
1 year ago
|
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
|
||
|
```go
|
||
|
func coordinate() (x, y float64, err error) {
|
||
1 year ago
|
// normal code
|
||
1 year ago
|
}
|
||
|
```
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
|
### 6.2 defer
|
||
|
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
|
||
|
```go
|
||
1 year ago
|
rep, err := http. Get(url)
|
||
1 year ago
|
if err != nil {
|
||
1 year ago
|
return err
|
||
1 year ago
|
}
|
||
|
|
||
|
defer resp.Body.Close()
|
||
|
```
|
||
|
|
||
1 year ago
|
### 6.3 Method Receiver
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
1 year ago
|
### 6.4 Nesting
|
||
|
- The nesting depth cannot exceed 4 levels.
|
||
1 year ago
|
|
||
1 year ago
|
### 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:
|
||
1 year ago
|
|
||
|
```go
|
||
1 year ago
|
// PI...
|
||
|
const Price = 3.14
|
||
1 year ago
|
|
||
|
func getAppleCost(n float64) float64 {
|
||
1 year ago
|
return Price * n
|
||
1 year ago
|
}
|
||
|
|
||
|
func getOrangeCost(n float64) float64 {
|
||
1 year ago
|
return Price * n
|
||
1 year ago
|
}
|
||
|
```
|
||
|
|
||
1 year ago
|
## 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.
|
||
1 year ago
|
|
||
1 year ago
|
## 8. Dependency Management
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
1 year ago
|
### 9. Best Practices
|
||
1 year ago
|
|
||
1 year ago
|
- 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:
|
||
1 year ago
|
|
||
|
```go
|
||
|
type LogHandler struct {
|
||
1 year ago
|
h http.Handler
|
||
|
log *zap. Logger
|
||
1 year ago
|
}
|
||
1 year ago
|
var_http.Handler = LogHandler{}
|
||
1 year ago
|
```
|
||
1 year ago
|
- 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.
|
||
1 year ago
|
|
||
1 year ago
|
### 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.
|
||
1 year ago
|
|
||
1 year ago
|
### 9.2 Precautions
|
||
1 year ago
|
|
||
1 year ago
|
- 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.
|