Naming rules

  • From the caller’s point of view, the package is not for your own use
  • Concise, and see the name know meaning
  • Use a general, well-known abbreviation named. Such asbufRather thanbufio
  • If the abbreviated name is ambiguous, drop it or change it

The file name

The main entry file for the entire application or package should be main.go, or the same as the short name of the application.

For example, the main entry file of the spiker package is spiker.go, and the main entry file of the application is main.go

The package name

  • The package name is the same as the directory name

    If multiple packages appear in a directory at the same time, compilation fails:

    found packages pkg (a.go) and pb (b.go) in XXX
    Copy the code
  • In most cases with named imports, renaming is not required

    Don’t let callers alias names unless they suck

  • All lowercase, no underscores, no caps. Error examples MyPackage, my_package, MyPackage

  • No complex numbers. For example, net/ URL, not net/urls

  • Don’t use names that aren’t informative. Error examples common, lib, util

Import packages

  • If the package name does not match the last element of the import path, the import alias must be used
import (
    client "example.com/client-go"
    trace "example.com/trace/v2"
)
Copy the code
  • In all other cases, import aliases should be avoided unless there is a direct conflict between imports
import (
    "net/http/pprof"
    gpprof "github.com/google/pprof"
)
Copy the code
  • In case of duplicate names, keep standard packages and alias custom or third party packages

  • In non-test files (*_test.go), disallow the use of. To simplify object calls to import packages

  • Do not use relative path import (./subpackage). All import paths must comply with the Go GET standard

Hump nomenclature

Constants, variables, types, structs, interfaces, functions, methods, properties, etc., all use hump MixedCaps or MixedCaps.

Names starting with an underscore are not allowed, and Go uses case to distinguish between public and private names.

With one exception, function names may contain underscores for grouping related test cases, such as TestMyFunction_WhatIsBeingTested.

Bad:

const ROLE_NAME = 10
Copy the code

Good:

const RoleName = 10
Copy the code

constant

  • If it is a constant of enumerated type, you need to create the corresponding type first
type Scheme string

const (
    Http  Scheme = "http"
    Https Scheme = "https"
)
Copy the code
  • If the functionality of the module is complex and constant names are confusing, use full prefixes to better distinguish enumerated types
type Symbol string

const (
    SymbolAdd Symbol = "+"
    SymbolSub Symbol = "-"
)
Copy the code

variable

  • In a relatively simple environment with a small number of targeted objects, you can abbreviate some names from full words to single letters

    • userI can write it as thetau
    • userIdCan be abbreviateduid
  • If the variable type Is bool, the name should start with Has, Is, Can, or Allow

var isExist bool
var hasConflict bool
var canManage bool
var allowGitHook bool
Copy the code

URL

  • The URL name is all lowercase
  • With a forward slash/Show hierarchy
  • Use a hyphen-To improve the readability of names in long paths
  • Underscores must not be used in urls_
  • Urls should not end with forward slashes/
  • File extensions should not be included in the URL
  • The URL must be known by name, but must not reveal the server architecture

Bad:

/GetUserInfo
/photos_path
/My-Folder/my-doc/
/user/user-list
Copy the code

Good:

/user/list
/user/operator-logs
Copy the code

Function/method name

  • Don’t gild the lily

  • Long names do not make them more readable, and a useful documentation is often more valuable than additional long names

Bad:

once.DoOrWaitUntilDone(f)
Copy the code

Good:

once.Do(f)
Copy the code
  • A function named New in the PKG package returns a value of type pkg.pkg
q := list.New()  // q is a *list.List
Copy the code
  • When a function in a PKG package returns a value of type pkg.pkg (or * PKG.pkg), the function name should omit the type name
start := time.Now()                                  // start is a time.Time
t, err := time.Parse(time.Kitchen, "6:06PM")         // t is a time.Time
Copy the code
  • When a function returns a value of type pkG. T and T is not PKG, the function name should include T to make the user code easier to understand
ticker := time.NewTicker(d)          // ticker is a *time.Ticker
timer := time.NewTimer(d)            // timer is a *time.Timer
Copy the code
  • Getter/setter

    Go does not provide automatic support for getters and setters. For a variable or field, the getter name does not carry Get; the setter name begins with Set.

    If you have a field named OWNER (lowercase, unexported), its getter should be owner (uppercase, exportable) instead of GetOwner.

Bad:

owner := obj.GetOwner()
ifowner ! = user { obj.SettingOwner(user) }Copy the code

Good:

owner := obj.Owner()
ifowner ! = user { obj.SetOwner(user) }Copy the code
  • If the function or method is of judgment type (the return value is primarily bool), the name should beHas,Is,CanAllowAnd so on
func HasPrefix(name string, prefixes []string) bool{... }func IsEntry(name string, entries []string) bool{... }func CanManage(name string) bool{... }func AllowGitHook(a) bool{... }Copy the code

The interface name

By convention, interfaces that contain only one method should be named with the method name suffix -er, such as Reader, Writer, Formatter/CloseNotifier, and so on.

Nouns are used for interface names and verbs are used for interface method names.

type Reader interface {
	Read(p []byte) (n int, err error)
}
Copy the code

Error

  • ErrorThe type is named afterErrorAt the end
type ParseError struct {
    Line, Col int
}
Copy the code
  • ErrorType variable toErrAt the beginning
var ErrBadAction = errors.New("somepkg: a bad action was performed")
Copy the code
  • Return type isErrorVariable abbreviation adoptederr
func foo(a) {
    res, err := somepkgAction()
    iferr ! =nil {
        if err == somepkg.ErrBadAction {
        }
        if pe, ok := err.(*somepkg.ParseError); ok {
             line, col := pe.Line, pe.Col
             / /...}}}Copy the code

other

The name of the package content cannot start with the package name, because the package name does not need to be repeated

The HTTP package provides an HTTP service named http.server, not HTTPServer. User code references this type through http.server, so there is no ambiguity.

The type names in different packages can be the same because clients can distinguish them by package names

For example, the library contains several types named Reader, including JPEG.Reader, bufio.Reader, and Csv.reader. Each package name with Reader is a good type name.

List of abbreviations

acronym instructions
ctx ContextOr related, for examplegin.Context

A semicolon

Go is actually a semicolon; But Go, like JavaScript, does not recommend adding a semicolon to the end of a single statement, because the compiler automatically adds a semicolon.

Statements like the following are perfectly fine:

go func(a) { for { dst <- <-src } }()
Copy the code

Typically, Go programs use semicolons only in places such as for loop clauses to separate initializers, conditions, and incremental elements. If you write multiple statements on a single line, separate them with semicolons.

iferr := f(); err ! =nil {
    g()
}
Copy the code

For this reason, the opening brace of a function or control statement should never be placed on the next line.

if i < f()  / / an error
{           / / an error
    g()
}
Copy the code

parentheses

Control structures (if, for, and switch) do not require parentheses, not syntactically

The document

README, project document, interface document, etc., Chinese document typesetting reference: Chinese copy typesetting refers to north

annotation

  • All exported objects must be annotated to indicate their purpose, while non-exported objects are annotated as appropriate

  • Use the singular and continuous tense uniformly if the object is countable and there is no specified number; use the plural otherwise

  • Package, function, method, and type annotations are all one complete sentence

  • The first letter of a sentence type must be uppercase, and the first letter of a phrase type must be lowercase

  • The single line length of a comment cannot exceed 80 characters. If it exceeds 80 characters, force a line break

  • Comments for exportable objects must begin with the name of the object

// FileInfo is the interface that describes a file and is returned by Stat and Lstat
type FileInfo interface{...// HasPrefix returns true if name has any string in given slice as prefix
func HasPrefix(name string, prefixes []string) bool{...Copy the code

Single line comment & multi-line comment

  • Two comment styles, single line comment //, multi-line comment /*… * /

  • Multiline comments are only used for package-level document comments, except for single-line comments. Package comments are typically placed in the doc.go file, which contains only the content of the document comments

  • Please separate a single line comment symbol from the content with a space

Bad:

//Comments
Copy the code

Good:

// Comments
Copy the code

GoLand can be set to automatic formatting:

Preferences > Editor > Code Style > Go > Other Check Add leading space to comments

Package comments

  • Package-level comments are an introduction to a package and need only be specified in any source file for the same package to be effective

  • For the main package, there is typically a one-line comment that describes the purpose of the package, starting with the project name

// Write project description
package main
Copy the code
  • For subpackages of a complex project, package-level annotations are generally not required, except for modules that represent a particular function

  • Simple non-main packages can also be summarized with a one-line comment

  • For relatively complex non-main packages, examples or basic instructions will be added, starting with Package

/* Package http provides HTTP client and server implementations. ... * /
package http
Copy the code
  • Extremely complex package specifications that can be created separatelydoc.goDocuments to illustrate

Functions and Methods

  • If one sentence is not enough, a line break can continue with a more detailed description
// Copy copies file from source to target path.
// It returns false and error when error occurs in underlying function calls.
Copy the code
  • If the function or method is of judgment type (the return value is mainlyboolType), then<name> returns true ifAt the beginning
// HasPrefix returns true if name has any string in given slice as prefix.
func HasPrefix(name string, prefixes []string) bool{...Copy the code

Structure, interface, and other types

  • Definitions of types are usually singular:
// Request represents a request to run a command.
type Request struct{...Copy the code
  • If it is an interface, it is generally described in the following format:
// FileInfo is the interface that describes a file and is returned by Stat and Lstat.
type FileInfo interface{...Copy the code
  • If the structure has many attributes, add comments to the attributes
// Var variable for expression
type Var struct {
    Key   string      `json:"key"`   // variable key
    Value interface{} `json:"value"` // value
    Desc  string      `json:"desc"`  // variable description
}
Copy the code

Other instructions

  • A comment at the beginning of TODO: can be used to alert maintenance personnel when a section is waiting to complete.

  • A comment at the beginning of FIXME: can be used to alert maintenance personnel when a part has known problems that need to be fixed or improved.

  • NOTE: When you need to specify something, you can use it.

// NOTE: os.Chmod and os.Chtimes don't recognize symbolic link,
// which will lead "no such file or directory" error.
return os.Symlink(target, dest)
Copy the code

formatting

We don’t have much of a choice because Go is already regulated and there are no such wars in the Go world.

The indentation

The indent must contain four Spaces and the TAB character is disabled.

EditorConfig Settings:

[{Makefile,go.mod,go.sum,*.go}]
indent_style = tab
indent_size = 4
Copy the code

Or GoLand Settings:

Preferences > Editor > Code Style > Go > Tabs and Indents

A blank line

  • Add blank lines to keep paragraphs clear

other

Grouping and ordering of functions

  • Functions should be ordered in rough order of invocation
  • Functions in the same file should be grouped by receiver
  • The exported function should appear in the file first, placed instruct,constvarAfter the definition.
  • One may appear after the type is defined, but before the rest of the receiver’s methodsnewXYZ() / NewXYZ().
  • Because functions are grouped by receiver, common utility functions should appear at the end of the file.
  • So, generally onestructAnd related methods are organized into a file.

Bad:

func (s *something) Cost(a) {
    return calcCost(s.weights)
}

type something struct{... }func calcCost(n int[]) int{... }func (s *something) Stop(a){... }func newSomething(a) *something {
    return &something{}
}
Copy the code

Good:

type something struct{... }func newSomething(a) *something {
    return &something{}
}

func (s *something) Cost(a) {
    return calcCost(s.weights)
}

func (s *something) Stop(a){... }func calcCost(n int[]) int{... }Copy the code

Reduce the nested

Bad:

for _, v := range data {
    if v.F1 == 1 {
        v = process(v)
        if err := v.Call(); err == nil {
            v.Send()
        } else {
            return err
        }
    } else {
        log.Printf("Invalid v: %v", v)
    }
}
Copy the code

Good:

for _, v := range data {
    ifv.F1 ! =1 {
        log.Printf("Invalid v: %v", v)
        continue
    }

    v = process(v)
    iferr := v.Call(); err ! =nil {
        return err
    }
    v.Send()
}
Copy the code

Unnecessary else

  • Most of the time, we can extract code from the else branch as initialization.

Bad:

var a int
if b {
    a = 100
} else {
    a = 10
}
Copy the code

Good:

a := 10
if b {
    a = 100
}
Copy the code

Global variable declaration

  • Global variables, must be usedvarThe keyword
  • Do not specify a type unless it is different from the type of the expression

Bad:

var a string = "abc"
var s string = F()
    
func F(a) string { return "A" }
Copy the code

Good:

var a = "abc"
Since F() already explicitly returns a string type, there is no need to explicitly specify the type of s
var s = F()
    
func F(a) string { return "A" }
Copy the code
  • Specify the type if the type of the expression does not exactly match the desired type
type myError struct{}

func (myError) Error(a) string { return "error" }

func F(a) myError { return myError{} }

var err error = F()
// F() returns an instance of type myError, but we want the error type
Copy the code

Local variable declaration

  • If a variable is explicitly set to a value, use the short variable declaration form (: =)

Bad:

var s string = "abc"
Copy the code

Good:

s := "abc"
Copy the code
  • If the variable is dedicated to a referencevarKeywords are more appropriate
func s(a) {
    var s string
    f(&s)
}
Copy the code
  • If the variable is a return value, it is defined in the function return type
func f(list []int) (filtered []int) {
    for _, v := range list {
        if v > 10 {
            filtered = append(filtered, v)
        }
    }
    return
}
Copy the code

Import Package import grouping and sorting

  • If multiple packages are imported into the same file, group them
  • Standard package, third party package, custom package, respectively grouped, empty line separated, sorted

Bad:

import "a"
import "golang.org/x/sys"
import "runtime"
import "github.com/gin-gonic/gin"
import "b"
import "fmt"
Copy the code

Good:

import (
    "fmt"
    "runtime"

    "a"
    "b"

    "github.com/gin-gonic/gin"
    "golang.org/x/sys"
)
Copy the code

GoLand Settings are as follows:

Group similar declarations

For declarations such as var, const, type, etc:

  • Put similar declarations in a group

Bad:

const a = 1
const b = 2

var a = 1
var b = 2

type Area float64
type Volume float64
Copy the code

Good:

const (
    a = 1
    b = 2
)

var (
    a = 1
    b = 2
)

type (
    Area float64
    Volume float64
)
Copy the code
  • Place only related declarations in a group, never unrelated declarations in a group

Bad:

type Operation int

const (
    Add Operation = iota + 1
    Subtract
    Multiply
    RoleName = "Role Name"
)
Copy the code

Good:

type Operation int

const (
    Add Operation = iota + 1
    Subtract
    Multiply
)

const RoleName = "Role Name"
Copy the code
  • There is no limit to where groups can be used, and groups can also be used within functions

Bad:

func f(a) string {
    var red = color.New(0xff0000)
    var green = color.New(0x00ff00)
    var blue = color.New(0x0000ff)

    // ...
}
Copy the code

Good:

func f(a) string {
    var (
        red   = color.New(0xff0000)
        green = color.New(0x00ff00)
        blue  = color.New(0x0000ff))// ...
}
Copy the code

Function/method argument/return type order

  • Simple types take precedence over complex types

Bad:

func F(u User, n int) {}
Copy the code

Good:

func F(n int, u User) {}
Copy the code
  • If possible, put parameters of the same type next to each other, then you only need to write the type once

Bad:

func F(a int, c string, b int) {}
Copy the code

Good:

func F(a, b int, c string) {}
Copy the code
  • errorAlways the last return type

Bad:

func F(a) (error, int) {}
Copy the code

Good:

func F(a) (int, error) {}
Copy the code

Order of structure attributes

Let’s start with an example:

Structure A – Definition:

struct {
	a string
	c string
	b bool
	d bool
}
Copy the code

Structure A – size 40, memory layout diagram:

Contrast, structure B – definition:

struct {
	a string
	b bool
	c string
	d bool
}
Copy the code

Structure B – size 48, memory layout diagram:

We found that the size of memory and layout of the structure were completely different depending on the order of its attributes.

So we have a convention: try to put attributes of the same type together. That is, the order of definitions in structure A is recommended.

Embeddedness in a structure

The embedded type (such as MUtex) should be at the top of the list of fields in the body of the structure, and there must be a blank line separating the embedded field from the regular field.

Bad:

type Client struct {
    version int
    http.Client
}
Copy the code

Good:

type Client struct {
    http.Client

    version int
}
Copy the code

The field name must be specified when initializing the structure

The field name must be specified when initializing the structure, otherwise the relevant tools and Review will not give it. If not specified, code refactoring can have unexpected consequences.

Bad:

k := User{"John"."Doe".true}
Copy the code

Good:

k := User{
    FirstName: "John",
    LastName: "Doe",
    Admin: true,}Copy the code

The only exception: if there are three or fewer fields, you can omit field names from the test table

tests := []struct{
}{
    op Operation
    want string
}{
    {Add, "add"},
    {Subtract, "subtract"}},Copy the code

Reduce the scope of variables

  • If possible, minimize the scope of a variable unless it conflicts with rules that reduce nesting

Bad:

err := ioutil.WriteFile(name, data, 0644)
iferr ! =nil {
    return err
}
Copy the code

Good:

if err := ioutil.WriteFile(name, data, 0644); err ! =nil {
    return err
}
Copy the code
  • If you need to use the results of a function call outside of if, you should not try to narrow it down

Bad:

if data, err := ioutil.ReadFile(name); err == nil {
    err = cfg.Decode(data)
    iferr ! =nil {
    return err
    }

    fmt.Println(cfg)
    return nil
} else {
    return err
}
Copy the code

Good:

data, err := ioutil.ReadFile(name)
iferr ! =nil {
    return err
}

iferr := cfg.Decode(data); err ! =nil {
    return err
}

fmt.Println(cfg)
return nil
Copy the code

Error messages should not be capitalized or punctuated

Bad:

fmt.Errorf("Something bad.")
Copy the code

Good:

fmt.Errorf("something bad")
Copy the code

The Error description needs to be wrapped or referenced, so the following code tells us why it should not:

log.Printf("Reading %s: %v", filename, err)
Copy the code

slice

Nil is a slice with a valid length of 0

  • Zero-value slices are available immediately without a call to make

Bad:

nums := []int{}
// or, nums := make([]int, 0)

if add1 {
    nums = append(nums, 1)}Copy the code

Good:

var nums []int

if add1 {
    nums = append(nums, 1)}Copy the code
  • To check if the slice is empty, always uselen(s) == 0, don’t checknil

Bad:

func isEmpty(s []string) bool {
    return s == nil
}
Copy the code

Good:

func isEmpty(s []string) bool {
    return len(s) == 0
}
Copy the code
  • For slices that need serialization, you must initialize them using make

Bad:

var v []int
s, _ := json.Marshal(v)
println(string(s))
// output: null
Copy the code

Good:

v := make([]int.0)
s, _ := json.Marshal(v)
println(string(s))
// output: []
Copy the code

The resources

  • Package names – Go Blog
  • Style guideline for Go packages
  • Organizing Go code – Go Blog
  • How to Write Go Code – Go Blog
  • Effective Go
  • Go Code Convention
  • Go Wiki – Errors
  • Uber Go Style Guide