golang编程规范

122 阅读6分钟

基本元素设计

[规则5-1]struct规范

  • *struct申明和初始化格式采用多行:起始申明占一行,每个成员占一行,结束}占一行; *如果 struct结构简单或者赋值字段1-2个,初始化可以使用一行。

正例:

type User struct {

        Username  string

        Email     string

        ID        int

        BirthDay  time.Time

        Age       int

        Gender    uint8

}



type Worker struct {

        Name String

        Age  int

}

反例:

type User struct

{

        Username  string;Email     string

        Id        int

        BirthDay  time.Time

        Age       int

        Gender    uint8

}

初始化示例:

正例:

// 多字段初始化

u := User{

        Username: "test",

        Email:    "test@gmail.com",

}

// 单行初始化

u := User{Username: "test"}

w := Worker{"test", 19}

反例:

u := User{Username:"test",Id:121212,BirthDay:20190909,Age:12,Gender:0} 
  • 如果 struct 中的数据变量需要进行 json 序列化,则需要以大写字母开头,同时需要 json 重命名。

说明: 结构体中的变量以大写字母开头,可以保证 json.Marshal 的时候数据持久化正确。如果结构体中的变量以小写字母开头,则使得 json.Marshal 的时候忽略该字段,使得该字段的值丢失,从而 json.Unmarshal 的时候将该变量的值置为默认值。由于结构体中的变量以大写字母开头, json串中的字段 key 的字符串形式变成了以大写字母开始,这对于追求以 json 串全小写为美的我们来说,需要进行json重命名。

建议:对于序列化的json结构来说,字符串字段慎用[]byte。([]byte序列化会被base64,会引起跨语言解析失败)

正例:

type Position struct {

        X int `json:"x"`

        Y int `json:"y"`

        Z int `json:"z"`

}

type Student struct {

        Name string `json:"name"`

        Sex string `json:"sex"`

        Age int `json:"age"`

        Posi Position `json:"position"`

}

反例:

type Position struct {

    X int

    Y int

    Z int

}

type Student struct {

    Name string

    Sex string

    Age int

    Posi Position

}

[规则5-2]if

  • 若 if语句不会执行到下一条语句时,即其执行体 以break、continue、goto或return结束时,else省略

正例:

if err := file.Chmod(0664); err != nil {

        log.Print(err)

        return err

}



f, err := os.Open(name)

if err != nil {

        return err

}

codeUsing(f)

反例:

  • if err := file.Chmod(0664); err != nil {
    
            log.Print(err)
    
            return err
    
    } else {
    
            xxxx
    
    }
    
    
    
    if f, err := os.Open(name); err != nil{
    
            return err
    
    } else {
    
            codeUsing(f)
    
    }
    

    对于布尔类型的变量,应直接进行真假判断

示例:

  • 正例
    
    if flag  /* 表示flag为真 */
    
    if !flag  /* 表示flag为假 */
    
    反例
    
    if flag == true
    
    if flag == false
    

    if 语句的嵌套层数不要太多,不大于3,超过优化代码逻辑

[规则5-3]for

  • for语句的嵌套层数不要太多,不大于3,超过优化代码逻辑
  • 使用for循环时,优先使用range 关键字而不是显式下标递增控制,除非需要显示修改某下标元素内容

示例:

```

正例 for i := 0; i < len(array); i++ { if i & 1 == 0 { array[i] += 1 } } for i, v := range array { fmt.Printf("element %v of array is %v\n", i, v) } 反例 for i := 0; i < len(array); i++ { fmt.Printf("element %v of array is %v\n", i, array[i]) }


-   逻辑表达式已经具有 true 或 false 语义,应尽量简洁

**示例:**

-   ```
    正例

    return i == 3



    反例

    if i == 3 {

            return true

    } else {

            return false

    }
    ```

    单目运算符之间不用空格,双目运算符之间用空格隔开;下列运算符之间不需要空格

    -   关系运算符:!
    -   位运算符: ^, >>, <<,
    -   其他:取地址运算符&, 指针运算符*

**示例:**

# ```
正例  x<<8 + y<<16  a & b  (*a).Field    反例  a+b  x << 8 + y << 16  a&b 
```函数设计

### [规则6-1]**函数命名**

-   要短小精悍和名副其实,避免误导。一般以它" 做什么" 来命名,而不是以它" 怎么做" 来命名

### [规则6-2]函数结构

-   函数应该做一件事,做好这件事,只做这一件事
-   ****函数要短小,包括空行和{},这里建议不超过80行,多了拆分多个函数
-   函数的缩进层次不应该超过3层

### [规则6-3]函数参数

-   函数参数个数应该限制,入参控制在5个内

    -   建议:出参控制在2个内

-   对于简单结构,不要传递指针;对于大结构数据的struct可以考虑使用指针

-   传入参数是map,slice,chan不要传递指针,因为map,slice,chan是引用类型,不需要传递指针的指针

-   参数列表中若干个相邻的参数类型相同,在参数列表中省略前面变量的类型声明

-   当 channel 作为函数参数时,根据最小权限原则,使用单向 channel

**示例:**

func Parse(ch <-chan int)

func After(d Duration) <-chan Time


**建议:** 形参和返回值在定义时带上名称,使代码更清晰

**示例:**

# ```
正例  func nextInt(b []byte, pos int) (value, nextPos int)  func ReadFull(r Reader, buf []byte) (n int, err error)    反例  func nextInt([]byte, int) (int, int)  func ReadFull(Reader, []byte) (int, error) 
```错误

### [规则7-1]使用规则

-   正确的使用error返回值,禁止将错误值返回(例如用-1表示EOF)和修改通过地址传入的实参
-   对于api中含有err返回值,禁止使用_忽略错误返回值

**示例:**

### ```
正例  data, err := json.Marshal(a)  if err != nil {          xxx  }    反例  data, _ := json.Marshal(a) 
```[规则7-2]错误设计

-   包内错误值统一分组定义,而不是零散的分布在各个角落

**正例:**

-   ```
    // file error objectvar (

            ErrEof = errors.New("EOF")

            ErrClosedPipe = errors.New("io: read/write on closed pipe")

            ErrShortBuffer = errors.New("short buffer")

            ErrShortWrite = errors.New("short write")

    )

    // port error objectvar (

            ErrAddPortToOvsBriFailed = errors.New("add port to ovs bri failed")

            ErrPortNotEnteredPromiscuousMod = errors.New("port not entered promiscuous mod")

        ...

    )
    ```

    失败的原因只有一个时,不使用error

-   当函数的error返回值始终是nil时,不使用error作为返回值

**正例:**

func (self *AgentContext) IsValidHostType(hostType string) bool {

    return hostType == "virtual_machine" || hostType == "bare_metal"

}


**反例:**

-   ```
    func (self *AgentContext) CheckHostType(hostType string) error {

            switch hostType {

            case "virtual_machine":

                return nil

            case "bare_metal":

                return nil

            }

            return ErrInvalidHostType

    }
    ```

    ****error/bool应放在返回值类型列表的最后

**正例:**

-   ```
    resp, err := http.Get(url)

    if err != nil {

        return nil, err

    }



    value, ok := cache.Lookup(key) 

    if !ok {

        // ...cache[key] does not exist… 

    }
    ```

    每一层捕获错误,如无必要,无需逐层传递

-   错误处理巧用defer

**正例:**

func deferDemo() error {

    err := createResource1()

    if err != nil {

            return ErrCreateResource1Failed

    }

    defer func() {

            if err != nil {

                    destroyResource1()

            }

    }()

    err = createResource2()

    if err != nil {

            return ErrCreateResource2Failed

    }

    defer func() {

            if err != nil {

                    destroyResource2()

            }

    }()



    err = createResource3()

    if err != nil {

            return ErrCreateResource3Failed

    }

    defer func() {

        if err != nil {

                destroyResource3()

        }

    }()



    err = createResource4()

    if err != nil {

            return ErrCreateResourc4Failed

    }

    return nil

}


**反例:**

-   ```
    func deferDemo() error {

            err := createResource1()

            if err != nil {

                    return ErrCreateResource1Failed

            }

            err = createResource2()

            if err != nil {

                    destroyResource1()

                    return ErrCreateResource2Failed

            }



            err = createResource3()

            if err != nil {

                    destroyResource1()

                    destroyResource2()

                    return ErrCreateResource3Failed

            }



            err = createResource4()

            if err != nil {

                destroyResource1()

                destroyResource2()

                destroyResource3()

                return ErrCreateResource4Failed

            }

            return nil

    }
    ```

    错误消息应以小写字母开头,不应以.结尾。为保持一致性,应对日志消息应用相同的逻辑。

示例:

### ```
正例  logger.Print("something went wrong")  ErrReadFailed := errors.New("could not read file")  反例  logger.Print("Something went wrong.")  ReadFailError := errors.New("Could not read file") 
```[规则7-3]panic&recover

-   尽量不要使用panic,除非你知道你在做什么
-   对于每个协程调用栈,routine1->routine2->routine3,使用defer加入recover,除非你能确保每个goroutine能够正常终止

**示例:**

# ```
func server(workChan <-chan *Work) {          for work := range workChan {                  go safelyDo(work)          }  }    func safelyDo(work *Work) {          defer func() {                  if err := recover(); err != nil {                          log.Println("work failed:", err)                  }          }()          do(work)  } 
```测试

### [规则8-1]测试设计

-   不要为了测试而对产品代码进行侵入性修改

**说明:** 禁止为了测试而在产品代码中增加条件分支或函数变量。

-   应当为每个线上生产代码补上单元测试,除非生产代码是很简洁通用的代码
-   测试应该是黑盒的。

**说明:** 避免根据代码编写测试。

# 考题

[golang编程规范考题](https://bytedance.feishu.cn/space/doc/doccnVgdmmLdEKImMLxJvJFHFPe)

# 参考:

https://github.com/golang/go/wiki/CodeReviewComments

https://golang.org/doc/effective_go.html

https://go-zh.org/doc/effective_go.html

https://go-zh.org/ref/spec

http://google.github.io/styleguide/

https://www.jianshu.com/p/4540bb8fc9b5

https://github.com/bahlo/go-styleguide

https://github.com/golang/go/tree/master/src