其他分享
首页 > 其他分享> > Golang编码规范

Golang编码规范

作者:互联网

文章目录

1. 前言

1.1. 一般信息【重要必读】

1.2. 如何使用本编程规范

本规范的层次结构

条目的级别和编号

1.3. 说明

参考下列规范并结合自己的业务场景

2. 语言规范

2.1 true/false 求值

示例:

// GOOD:
var isWhiteCat bool
var num int
if isWhiteCat {
  //
}
if num == 0 {
  // ...
}
//BAD:
var isWhiteCat bool
var num int
if isWhite == true {
  // ...
}
if !num {
  // ...
}

2.2 Receiver

2.2.1 Receiver Type

解释

2.2.2 receiver 命名

示例

//GOOD:
// call()和 done()都使用了在上下文中有意义的"c"进行 receiver 命名
func (c Client) call() error {
  // ...
}
func (c Client) done() error {
  // ...
}
//BAD:
// 1. "c"和"client"命名不一致:done()用了 c,call()用了 client
// 2. client 命名过于冗余
func (c Client) done() error {
  // ...
}
func (client Client) call() error {
  // ...
}
// 不允许使用 self
func (self Server) rcv() error {
  // ...
}
// 不允许使用 this
func (this Server) call() error {
  // ...
}

2.3 类型申明

//GOOD:
var t []string
// 指定容量 
res := make([]string, 0 ,10)
//定义如下:
type User struct{
  Username string
  Email   string
}
u := User{
  Username: "astaxie",
  Email:   "webben@gmail.com",
}
//BAD:
t := []string{}
res := make([]string)

2.4 Error Handler

//GOOD:
if inputIo, err := ioutil.ReadAll(ctx.Request.Body); err != nil {
  // handling
}
if err != nil {
  // error handling
  return // or continue, etc.
}
// normal code
// 如果返回值需要初始化,则采用下面的方式
x, err := f()
if err != nil {
  // error handling
  return
}
// use x
//BAD:
// 忽略err不可采取
inputIo, _ := ioutil.ReadAll(ctx.Request.Body)
if err != nil {
  // error handling
} else {
  // normal code
}

2.5 自定义类型的String循环问题

type MyInt int 
// GOOD:
func(m MyInt) String() string {  
  return fmt.Sprint(int(m))   //这是安全的,因为我们内部进行了类型转换
}
//BAD:
func (m MyInt) String() string {  
  return fmt.Sprint(m)   //BUG:死循环
}

2.6 Typical Data Races-数据竞争

​ 参考:http://golang.org/doc/articles/race_detector.html#Race_on_loop_counter

//GOOD:
func main() { 
  var wg sync.WaitGroup 
  wg.Add(5) 
  for i := 0; i < 5; i++ { 
    go func(j int) {     
      fmt.Println(j)  // Good. Read local copy of the loop counter.     
      wg.Done() 
    }(i)
  } 
  wg.Wait()
} 
func main() {   
  f1, err := os.Create("file1") 
  if err != nil { 
    res <- err 
  } else { 
    go func() {      
      // This err is shared with the main goroutine,      
      // so the write races with the write below.     
      _, err := f1.Write(data)     
      res <- err     
      f1.Close() 
    }() 
  } 
} 
//BAD:
func main() { 
  var wg sync.WaitGroup 
  wg.Add(5) 
  for i := 0; i < 5; i++ { 
    go func() {     
      fmt.Println(i)  
      // Not the 'i' you are looking for.     
      wg.Done()
    }()
  } 
  wg.Wait()
} 
func main() {
  f1, err := os.Create("file1") 
  if err != nil { 
    res <- err 
  } else {
    go func() {
      // This err is shared with the main goroutine,
      // so the write races with the write below. 
      _, err = f1.Write(data)
      res <- err
      f1.Close()
    }()
  }
}

2.7 引用第三包需要验证

2.8 字符串使用注意事项

2.9 embedding 的使用

解释

示例

//GOOD:
type Automobile struct { 
  // ...
} 
type Engine struct { 
  // ....
} 
// 正确的定义
type Car struct { 
  Automobile     //  Car is a Automobile engine engine   
  Engine  // Car has a Engine
}
//BAD:
type Car struct { 
  Automobile  //  Car is a Automobile 
  Engine    //  Car has a Engine, but Car is NOT a Engine
}

2.10 完善单元测试和性能测试

if got != tt.want {
  t.Errorf("Foo(%q) = %d; want %d", tt.in, got, tt.want) 
  // or Fatalf, if test can't test anything more past this point
}

2.11 业务需要梳理接口IO消耗

3. 风格规范

3.1 Go 文件 Layout

示例

//GOOD: 
/* Copyright 2015  Webben Inc. All Rights Reserved. */
/* bfe_server.go - the main structure of bfe-server */ 
/* modification history -------------------- 2014/6/5, by Zhang San, create*/
/* DESCRIPTION This file contains the most important struct 'BfeServer' of go- bfe and new/init method of the struct.*/ 
package bfe_server 

// imports
import ( 
  "fmt" 
  "time"
  
  "code.google.com/p/log4go"
  
  "bfe_config/bfe_conf"
  "bfe_module"
)

const (
  version = "1.0.0.0"
) 

// typedefs
type BfeModule interface { 
  Init(cfg bfe_conf.BfeConfig, cbs *BfeCallbacks) error
} 
type BfeModules struct {
  modules map[string]BfeModule
}

// vars
var errTooLarge = errors.New("http: request too large") 

//functions
func foo() { 
  //...
}

3.2 General Documentation Layout

解释

示例

//GOOD: 
/* Copyright 2015  Webben Inc. All Rights Reserved. */
/* bfe_server.go - the main structure of bfe-server  */ 
/* modification history -------------------- 2014/6/5, by Zhang San, create*/ 
/* DESCRIPTION This file contains the most important struct 'BfeServer' of go- bfe and new/init method of the struct.*/
package bfe_server func func1() {
  // ...
}
//BAD:
package bfe_server func func1() { 
  // ...
}

3.3 import 规范

示例:

//GOOD:
import ( 
  "fmt" 
  "time"
) 

import (
  "code.google.com/p/log4go"
) 
import (
  "bfe_config/bfe_conf"
  "bfe_module"
)

//GOOD:
import (
  "fmt"
  "time"
  
  "code.google.com/p/log4go"
  
  "bfe_config/bfe_conf"
  "bfe_module"
) 
//BAD: 
// 同一类 package 内 import 顺序需按字母升序排序
import (
  "time"
  "fmt"
) 
// 不同类的 package import 顺序出错(先第三方 package,再程序自己的 package)
import (
  "bfe_config/bfe_conf"
  "bfe_module"
) 
import (
  "code.google.com/p/log4go"
) 
//BAD:
import (
  // 同一类 package 内 import 顺序出错
  "time"
  "fmt"      
  // 不同类的 package import 顺序出错(先第三方 package,再程序自己的 package)   
  "bfe_config/bfe_conf"
  "bfe_module"
  "code.google.com/p/log4go"
)

3.4 Go 函数 Layout

3.4.1 函数注释

示例

/**  
 Init - initialize log lib
 **  PARAMS:
 *  - levelStr: "DEBUG", "TRACE", "INFO", "WARNING", "ERROR", "CRITICAL"
 *  - when: "M", minute*           "H", hour*           "D", day
 *           "MIDNIGHT", roll over at midnight
 *  - backupCount: If backupCount is > 0, when rollover is done, no more than
 *           backupCount files are kept - the oldest ones are deleted.
 **  RETURNS:
 *     nil, if succeed
 *     error, if fail
 **/
func Init(levelStr string, when string, backupCount int) error { 
  // ...
}

3.4.2 函数参数和返回值

示例:

//GOOD:
type student struct { 
  name     string 
  email     string 
  id        int 
  class string
} 
// bool 作为逻辑判断型函数的返回值
func isWhiteCat() bool { 
  // ...
} 
// error 作为操作型函数的返回值
func deleteData() error { 
  // ...
} 
// 利用多返回值的语言特性
func getData() (student, error) {     
  // ...
}
// BAD:
type student struct { 
  name     string 
  email     string 
  id        int 
  class string
} 
// 使用 int 而非 bool 作为逻辑判断函数的返回值
func isWhiteCat() int { 
  // ...
} 
// 操作型函数没有返回值
func deleteData() { 
  // ...
} 
// 没有充分利用 go 多返回值的特点
func getData() student { 
  // ...
} 
// 返回值>3
func getData() ( string, string, int, string, error) { 
  // ...
}

3.5 程序规模

解释

3.6 命名规范

3.6.1 文件名

示例:

//GOOD: 
// 可以使用下划线分割文件名web_server.go 
// 文件名全部小写http.go 
//BAD: 
// 文件名不允许出现大写字符webServer.go 
// 文件名后缀不允许出现大写字符http.GO

3.6.2 函数名/变量名

示例:

//GOOD: 
// 本 package 可以访问的函数
func innerFunc() bool { 
  // ...
} 
// 其余 package 可以访问的函数
func OuterFunc() error { 
  // ...
} 
//BAD: // 禁止用下划线分割
func inner_Func() bool { 
  var srv_name string 
  // ...
} 
// 禁止用下划线分割 
// 其余 package 可以访问的函数
func Outer_Func() error { 
  // ...
}

3.6.3 常量

解释

示例

//GOOD: 
// 大写字母
const METHOD = "Get" 
// 下划线分割
const HEADER_USER_AGENT = "User-Agent" 
// go 标准库中的命名方式
const defaultUserAgent = "Go 1.1 package http" 
//BAD: 
// 全部为下划线分割的小写字母
const header_user_agent = "User-Agent"

3.6.4 缩写词

示例:

//GOOD:
var URL string 
var ID int
var appID int 
type ServeURL struct { 
  // ...
} 
//BAD:
var Url string 
// not consistent
var ID int
var appid int 
type ServUrl struct { 
  // ...
}

3.7 缩进

解释

3.8 空格

//GOOD:
var ( 
  s = make([]int, 10)
) 
func foo() { 
  m := map[string]string{"language": "golang"} 
  r := 1 + 2 
  func1(1+2) 
  fmt.Println(m["language"])
} 
//BAD:
var ( s = make( []int , 10 )) 
func foo() { 
  m := map[string]string{ "language" : "golang" } 
  r := 1+2 
  func1(1 + 2) 
  fmt.Println(m[ "language" ])
}

3.9 括号

//GOOD:
if x {} 
func func1() int { 
  var num int 
  return num
} 
//BAD:
if (x) {
  
}
func func1 int { 
  var num int 
  return (num)
}

3.10 注释

解释

示例

//GOOD: 
/* This is the correct format for a single-line comment */ 
// This is the correct format for a single-line comment 
/**  This is the correct format for a multiline comment*  in a section of code.*/ 
// This is the correct format for a multiline comment 
// in a section of code. var a int 
// this is the correct format for a        
// multiline comment in a declaration  
//BAD: 
/* This is an incorrect format for a multiline comment*  in a section of code.*/ 
var a int /* this is an incorrect comment format */

4. 编程实践

4.1 error string

//GOOD:
fmt.Errorf("something bad") 

//BAD:
fmt.Errorf("Something bad")

4.2 Don’t panic, Do Recover

4.3 关于 lock 的保护

​ 这种情况下,即使 panic 不会导致整个程序的奔溃,也会由于”锁不释放“的问题而使临界区无法被后续的调用访问

示例

//GOOD:
func doDemo() { 
  lock.Lock() 
  defer lock.Unlock() 
  // 访问临界区
} 
//BAD:
func doDemo() { 
  lock.Lock() 
  // 访问临近区 
  lock.Unlock()
}
func doDemo() { 
  lock.Lock() 
  // step1: 临界区内的操作 
  lock.Unlock() 
  // step2: 临界区外的操作
} 
//如果改造为 defer 的方式,变为如下代码,实际上扩大了临界区的范围(step2 的操作也被放置在临界区了)
func  doDemo() { 
  lock.Lock() 
  defer  lock.Unlock()
  // step1: 临界区内的操作 
  // step2: 临界区外的操作
} 
  //优化:需要使用单独的匿名函数,专门用于访问临界区:
func  doDemo() { 
  func() {        
    lock.Lock()        
    defer lock.Unlock()         
    // step1: 临界区内的操作操作 
  }() 
  // step2: 临界区外的操作
}

4.4 日志的处理

4.5 unsafe package

示例

// ToString 把 []byte 转换为 string 没有多余的内存开销。 
// 使用该方法需要了解到 []byte 将和 string 公用一块内存, 修改 []byte 的数据将导致 string 发生变化, 
// 这打破了字符串不可以修改的特性,如果你恰恰需要这么做,可能非常的有用。 
// 要保证字符串不可变的特性,就必须保证 []byte 不会发生变化,或者立即消费 string,
func ToString(s []byte) string { 
  return *(*string)(unsafe.Pointer(&s))
} 
//使用该函数要明确两个事情: 
//  - 确定字符串是否是字面量,或者内存被分配在只读空间上。 
//  - 确保访问该函数结果的函数是否会按照你的意愿访问或修改数据。
func UnsafeToBytes(s string) []byte { 
  strHeader := (*reflect.StringHeader)(unsafe.Pointer(&s)) 
  return *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ 
    Data: strHeader.Data, 
    Len: strHeader.Len, 
    Cap: strHeader.Len, 
  }))
}

4.6 避免Golang函数传参引发的值拷贝

4.7 业务代码中禁止使用直接开协程,管道

4.8 Slice使用的注意事项

4.9 类型断言

4.10 代码复用及抽象化设计

BAD:

4.11 定义静态变量替换代码硬编码

//GOOD:
type cacheLevel int
const ( 
  //多级缓存开启策略配置 
  USE_ONLY_STORED cacheLevel = 1  //仅仅开启第二层stored分布式缓存,关闭第三层redis缓存 
  USE_ONLY_REDIS  cacheLevel = 2  //仅仅开启第三层redis缓存,关闭第二层stored分布式缓存 
  USE_STORED_REDIS cacheLevel = 3 //开启第二层stored,第三层redis缓存
) 
if level == USE_ONLY_STORED { 
  //...
} 
//BAD: 
if level == 1 { 
  //...
}

4.12 这份文档存在的意义是让大家写出统一风格的代码,让模块可维护性和可读性更好;

标签:编码,string,bfe,...,使用,规范,Golang,func,error
来源: https://blog.csdn.net/Webben/article/details/111587610