Golang编码规范

golang 编程规范 - Effective Go 中文 Go 是一门全新的语言。尽管它从现有的语言中借鉴了许多思想,但其与众不同的特性,使得用 Go 编程在本质上就不同于其它语言。将现有的 C++ 或 Java 程序直译为 Go 程序并不能令人满意 —— 毕竟 Java 程序是用 Java 编写的,而不是 Go。另一方面,若从 Go 的角度去分析问题,你就能编写出同样可行但大不相同的程序。换句话说,要想将 Go 程序写得好,就必须理解其特性和风格。了解命名格式化程序结构等既定规则也同样重要,这样你编写的程序才能更容易被其他程序员所理解。 阅读详情

1. 前言

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

  • 文档内容可能会与您的喜好冲突, 请尽量用包容的心态来接受; 不合理之处, 请评论指出。

1.2. 如何使用本编程规范

本规范的层次结构

  • 本规范可分为三大部分,分别对 Go 语法、风格、编程实践作出规定与建议。
  • 每一部分有若干专题,每一专题下有若干条目。
  • 条目是规范的基本组成部分,每一条目由规定、定义、解释、示例、参考等项组成。

条目的级别和编号

  • 本规范的条目分两个级别
    • RULE:要求所有程序必须遵守,不得违反
    • ADVICE:建议遵守,除非确有特殊情况
    • 本规范所有条目都有编号,用来标识单条规范并用于自动化检查工具错误提示。
      • 标号由"RULE"或"ADVICE"和三位数字标识,例如 RULE001, ADVICE001

1.3. 说明

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

  • 参考 “百度 Golang 编程规范V1.1”
  • 参考 https://golang.org/doc/effective_go.html
  • 参考 https://github.com/golang/go/wiki/CodeReviewComments

2. 语言规范

2.1 true/false 求值

  • {RULE001} 当明确 expr 为 bool 类型时,禁止使用 == 或 != 与 true/false 比较,应该使用 expr 或 !expr

  • {RULE002} 判断某个整数表达式 expr 是否为零时,禁止使用 !expr,应该使用 expr == 0

示例:

// 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

  • {RULE003} 如果 receiver 是 map、函数或者 chan 类型,类型不可以是指针
  • {RULE004} 如果 receiver 是 slice,并且方法不会进行 reslice 或者重新分配 slice,类型不可以是指针
  • {RULE005} 如果 receiver 是 struct,且包含 sync.Mutex 类型字段,则必须使用指针避免拷贝。
  • {RULE006} 如果 receiver 是比较大的 struct/array,建议使用指针,这样会更有效率
  • {ADVICE001} 如果 receiver 是 struct、array 或 slice,其中指针元素所指的内容可能在方法内被修改,建议使用指针类型
  • {ADVICE002} 如果 receiver 是比较小的 struct/array,建议使用 value 类型

解释

  • 关于 receiver 的定义详见 Receiver 定义:The receiver is specified via an extra parameter section preceeding the method name. That parameter section must declare a single parameter, the receiver. Its type must be of the form T or *T (possibly using parentheses) where T is a type name. The type denoted by T is called the receiver base type; it must not be a pointer or interface type and it must be declared in the same package as the method. The method is said to be bound to the base type and the method name is visible only within selectors for that type.

  • struct 或者 array 中的元素个数超过 3 个,则认为比较大,反之,则认为比较小

2.2.2 receiver 命名

  • {ADVICE003} 尽量简短并有意义。
  • {RULE007} 禁止使用“this"、”self“等面向对象语言中特定的叫法。
  • {ADVICE004} 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 类型申明

  • {RULE008} 申明 slice 时,建议使用 var 方式申明,不建议使用大括号的方式,如果使用make方式,最好能够预估slice的大小和容量
    解释:var 方式申明在 slice 不被 append 的情况下避免了内存分配示例,make指定容量会避免append扩容
  • {RULE009} struct声明和初始化格式采用多行
  • {RULE010} map,chan 均使用make方式初始化,可以理解返回的是个指针,slice有点区别,在扩容时,地址会变化
//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

  • {RULE011} 对于返回值中的 error,一定要进行判断和处理,不可以使用 ”_“ 变量忽略 error

  • {RULE012} 逻辑处理中禁用panic,对于其他的package对外的接口不能有panic,只能在包内采用。

  • {RULE012} 错误描述如果是英文必须为小写,不需要标点结尾

  • {RULE013} error的处理采用独立的错误流进行处理

//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循环问题

  • {RULE014} 自定义的类型定义了String方法,那么在打印的时候会产生隐藏的一些bug
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

  • {RULE015} Race on loop counter:注意闭包的调用,在循环中调用函数或者goroutine方法,一定要采用显示的变量调用,不要再闭包函数里面调用循环的参数,

  • {RULE016} Accidentally shared variable:协程中外层共享变量的使用,如外层中使用的err,协程中依然使用

  • {RULE017} Unprotected global variable:全局变量如果有修改的操作,需要进行加锁,如Map

//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 引用第三包需要验证

  • {RULE018}业务中引入任何第三包之前,需要调研对比各个包之间的性能差异,使用范围(如star数量)

    如json包:官方包encoding/json的性能较差,选用: jsoniter “github.com/json-iterator/go” 效率更高

2.8 字符串使用注意事项

  • {RULE019} 避免循环内进行字符串拼接时,如果避免不了,采用bytes.Buffer方式,预估内部的Grow次数,避免内存的频繁申请
  • {RULE020} 字符串的传值是传引用,任何修改都会导致重新分配空间
  • {RULE021} 少量字符串可使用+或string包操作,尽量少用fmt.Sprintf
  • {RULE022} 大量字符串的传参要传指针

2.9 embedding 的使用

  • {ADVICE005} embedding 只用于"is a"的语义下,而不用于"has a"的语义下
  • {ADVICE006} 一个定义内,多于一个的 embedding 尽量少用

解释

  • 语义上 embedding 是一种“继承关系“,而不是”成员关系“
  • 一个定义内有多个 embedding,则很难判断某个成员变量或函数是从哪里继承得到的
  • 一个定义内有多个 embedding,危害和在 python 中使用”from xxx import *"是类似的

示例

//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 完善单元测试和性能测试

  • 测试应该会失败,并提供有用的消息,说明错误,输入内容,实际内容以及预期结果。gotest
  • 公共功能函数应该做下性能测试,了解关键节点的IO代价,benchmark
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消耗

  • 在访问stored,redis,da等资源时,需要梳理业务接口的IO代价,及时调整设计实现,实现IO最小化

3. 风格规范

3.1 Go 文件 Layout

  • {RULE023} 建议文件按以下顺序进行布局

    • General Documentation: 对整个模块和功能的完整描述注释,写在文件头部。
    • package:当前 package 定义
    • imports:包含的头文件
    • Constants:常量
    • Typedefs: 类型定义
    • Globals:全局变量定义
    • functions:函数实现
      • public functions :首字符大写的函数
      • private functions:首字母小写的函数
  • {RULE024} 对于以上的各个部分,采用单个空行分割,同时:

    • 多个类型定义采用单个空行分割
    • 多个函数采用单个空行分割
  • {ADVICE007} 函数内不同的业务逻辑处理建议采用单个空行分割

  • {ADVICE008} 常量或者变量如果较多,建议按照业务进行分组,组间用单个空行分割

示例

//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

  • {ADVICE009} 建议每个文件开头部分包括文件 copyright 说明(copyright)
  • {ADVICE010} 建议每个文件开头部分包括文件标题(Title)
  • {ADVICE011} 建议每个文件开头部分包括修改记录(Modification History)
  • {ADVICE012} 建议每个文件开头部分包括文件描述(Description)

解释

  • Title 中包括文件的名称和文件的简单说明

    • Title 应该在一行内完成
  • Modification History 记录文件的修改过程,并且只记录最主要的修改

    • 当书写新的函数模块时,只需要使用形如"Add func1()"这样的说明
    • 如果后面有对函数中的算法进行了修改,需要指出修改的具体位置和修改方法
    • Modification History 的具体格式为:<修改时间>, <修改人>, <修改动作 >
  • Description 详细描述文件的功能和作用

示例

//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 规范

  • {RULE025} 需要按照如下顺序进行头文件 import,并且每个 import 部分内的 package 需按照字母升序排列
    • 系统 package
    • 第三方的 package
    • 程序自己的 package
  • {RULE026} 每部分 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 函数注释

  • {ADVICE013} 函数的注释,建议包括以下内容
    • Description:对函数的完整描述,主要包括函数功能和使用方法
    • Params:对参数的说明
    • Returns:对返回值的说明

示例

/**  
 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 函数参数和返回值

  • {ADVICE014} 对于“逻辑判断型”的函数,返回值的意义代表“真”或“假”,返回值类型定义为 bool
  • {ADVICE015} 对于“操作型”的函数,返回值的意义代表“成功”或“失败”,返回值类型定义为 error
    • 如果成功,则返回 nil
    • 如果失败,则返回对应的 error 值
  • {ADVICE016} 对于“获取数据型”的函数,返回值的意义代表“有数据”或“无数据/获取数据失败”,返回值类型定义为(data, error)
    • 正常情况下,返回为:(data, nil)
    • 异常情况下,返回为:(data, error)
  • {RULE027} 函数返回值小于等于 3 个,大于 3 个时必须通过 struct 进行包装
  • {ADVICE017} 函数参数不建议超过 3 个,大于 3 个时建议通过 struct 进行包装

示例:

//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 程序规模

  • {RULE028} 每行代码不超过 100 个字符。
  • {RULE029} 每行注释不超过 100 个字符。
  • {ADVICE018} 函数不超过 100 行。
  • {ADVICE019} 文件不超过 2000 行。

解释

  • 现在宽屏比较流行,所以从传统的 80 个字符限制扩展到 100 个字符
  • 函数/文件太长一般说明函数定义不明确/程序结构划分不合理,不利于维护

3.6 命名规范

3.6.1 文件名

  • {RULE030} 文件名都使用小写字母,如果需要,可以使用下划线分割
  • {RULE031} 文件名的后缀使用小写字母

示例:

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

3.6.2 函数名/变量名

  • {RULE032} 采用驼峰方式命名,禁止使用下划线命名。首字母是否大写,根据是否需要外部访问来决定

示例:

//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 常量

  • {ADVICE020} 建议都使用大写字母,如果需要,可以使用下划线分割
  • {ADVICE021} 尽量不要在程序中直接写数字,特殊字符串,全部用常量替代

解释

  • go 标准库中常量也有驼峰的命名方式,故这里不做强制限制。

示例

//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 缩写词

  • {RULE033} 缩写词要保持命名的一致性。
    • 同一变量字母大小写的一致性
    • 不同变量间的一致性

示例:

//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 缩进

  • {RULE034} 使用 tab 进行缩进。
  • {RULE035} 跨行的缩进使用 gofmt 的缩进方式。
  • {RULE036} 设置 tabstop=4

解释

  • 要求设置 tabstop=4 是考虑到不同编辑器跨行字符串对齐显示的一致性

3.8 空格

  • {RULE037} 圆括号、方括号、花括号内侧都不加空格
  • {RULE038} 逗号、冒号(slice 中冒号除外)前不加空格,后边加一个空格
  • {RULE039} 所有二元运算符前后各加一个空格(作为函数参数时除外)
//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 括号

  • {ADVICE022} 除非用于明确算术表达式优先级,否则尽量避免冗余的括号
//GOOD:
if x {} 
func func1() int { 
  var num int 
  return num
} 
//BAD:
if (x) {
  
}
func func1 int { 
  var num int 
  return (num)
}

3.10 注释

  • {ADVICE023} 单行注释,采取"//“或者”/…/"的注释方式。
  • {ADVICE024} 多行注释,采取每行开头“//“或者用”/* … /“包括起来的注释(”/“和”*/"作为独立的行)
  • {ADVICE025} 紧跟在代码之后的注释,使用"//"

解释

  • 大多数情况下,使用”//"更方便

示例

//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

  • {ADVICE026} error string 尽量使用小写字母,并且结尾不带标点符号解释
  • 因为可能 error string 会用于其它上下文中示例
//GOOD:
fmt.Errorf("something bad") 

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

4.2 Don’t panic, Do Recover

  • {RULE040} 除非出现不可恢复的程序错误,不要使用 panic,用多返回值和 error。
  • {RULE041} 避免大量使用Recover,在比较重要的代码中增加 recover 代码,将错误截获到并自己处理,如gin框架中Render数据的时候会抛出panic,这时候需要捕获panic,避免服务宕机

4.3 关于 lock 的保护

  • {ADVICE026} 如果临界区内的逻辑较复杂、无法完全避免 panic 的发生,则要求适用 defer 来调用Unlock,即使在临界区过程中发生了 panic,也会在函数退出时调用 Unlock 释放锁

    解释:go 提供了 recover,可以对 panic 进行捕获,但如果 panic 发生在临界区内,则可能导致对锁的使用没有释放

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

示例

//GOOD:
func doDemo() { 
  lock.Lock() 
  defer lock.Unlock() 
  // 访问临界区
} 
//BAD:
func doDemo() { 
  lock.Lock() 
  // 访问临近区 
  lock.Unlock()
}
  • {ADVICE027} 上述操作如果造成临界区扩大后,需要建立单独的一个函数访问临界区。

    对于如下的代码:

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 日志的处理

  • {ADVICE028} 日志打印会损耗性能,在关键节点上打印信息,区分Warn和Error,强制需要立即报错的使用Error,Warn用于一段时间内累计的错误报警

4.5 unsafe package

  • {ADVICE029} 除非特殊原因,不建议使用 unsafe package
    • 比如进行指针和数值 uintptr 之间转换就是一个特殊原因
    • 临时变量使用,并且内存占用很大,代码逻辑上不会修改和传入未知的三方包进行任何的修改和访问,如string和[]byte的转换,go-common基础组件中封装utils/extend方法,用于操作这类情况的处理,具体使用看业务场景

示例

// 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函数传参引发的值拷贝

  • {RULE042} 遵守 2.2 receiver 规则,对于大字符串,使用指针传参,对于大的struct切片,使用[]*struct声明使用方式,避免拷贝

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

  • {RULE043} 业务代码中禁止直接使用go关键字,避免启动的协程不可控,使用 go-common 包提供的协程池进行相关的操作,限制收敛协程的数量,协程池使用说明:[gouroutine使用说明]

  • {RULE044} 业务代码中禁止使用chan管道,避免造成死锁和性能损失,有需要使用管道的地方,向基础组件同学提相关的需求

4.8 Slice使用的注意事项

  • {RULE045} 使用常量值做为slice的索引取值时,要注意越界问题,需要判断len(arr) > index

  • {RULE046} 了解slice的append底层原理,容量不够时会另开空间,在函数传递时使用slice,注意函数内部使用append后,另开空间,函数外层无感知

4.9 类型断言

  • {RULE047} 对interface进行类型的断言时,必须先进行判断

4.10 代码复用及抽象化设计

  • {ADVICE030}进行代码复用,提炼抽象公共模块设计实现复杂功能

BAD:

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

  • {RULE}多定义静态变量,取代代码中的硬编码,增强代码的可以阅读性,
//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 这份文档存在的意义是让大家写出统一风格的代码,让模块可维护性和可读性更好;

Golang(二):使用Go语言创建并运行一个万年Hello World、Go语言编码规范 一、Hello World 1、创建项目 GoLand下:new—》project—》 创建好以后,只是个空项目: 2、编辑运行/调试配置 点击 Configurations ,进入设置启动项的内容 选择 Go Build: 关于下面配置项的一些说明: Name:为配置信息项的名称,配置好后,显示这样: Run kind:设置为“Directory” Direc... 阅读详情

相关推荐

libopus 实现pcm 编码到opus

opus 是一种音频格式,常用于语音通话、视频会议中。最近做了个pcm 到opus 的编码,踩了不少坑,特此记录一下。

sinat_27720649的博客 9464

Golang 编码规范

因为, 这些字符串可能和其他字符串相连接, 组合后的字符串如果中间有大写字母开头的单词很突兀, 除非这些首字母大写单词是固定使用的单词。如果想在每个文件的头部加上注释, 需要在版权注释和 Package 前面加一个空行, 否则版权注释会作为 package 的注释。注: 注释应该用一个完整的句子, 注释的第一个单词应该是要注释的指示符, 以便在 godoc 中容易查找;包名应该使用单数形式, 比如 util,model, 而不是 utils,models;.........

云满笔记 1524

C# WPF多页面间的数据共享(经典)

实现页面间的数据共享有很多方法,比如:①事件的发布订阅 ②委托③ioc容器④静态类等,这节主要通过静态的集合实现数据的添加和获取。

sf315418的博客 638

go语言使用规范

编码规范 本规范旨在为日常Go项目开发提供一个代码的规范指导,方便团队形成一个统一的代码风格,提高代码的可读性,规范性和统一性。本规范将从命名规范,注释规范,代码风格和 Go 语言提供的常用的工具这几个方面做一个说明。该规范参考了 go 语言官方代码的风格制定。 一、 命名规范 命名是代码规范中很重要的一部分,统一的命名规则有利于提高的代码的可读性,好的命名仅仅通过命名就可以获取到足够多的信息。 Go在命名时以字母a到Z或a到Z或下划线开头,后面跟着零或更多的字母、下划线和数字(0到9)。Go不允许在命名时

andrewbytecoder 1040

时频分析之STFT:短时傅里叶变换的原理与代码实现(非调用Matlab API)

STFT function [STFT, f, t] = mystft(x, win, hop, nfft, fs) % function: [STFT, f, t] = stft(x, win, hop, nfft, fs) % % Input: % x - signal in the time domain % win - analysis window function % hop - hop size % nfft - numb

frostime的博客 13万+

ADS 2023 实战:手把手教你仿真一个中心频率 2.4GHz 的带通滤波器(附工程文件)

本文详细介绍了使用ADS 2023软件设计和仿真中心频率2.4GHz带通滤波器的全流程,包括工程准备、指标定义、原理图设计、仿真优化及实际电路实现。通过切比雪夫滤波器拓扑和参数优化,实现高性能的带通滤波器设计,适用于无线通信系统。附有工程文件和常见问题解决方案,助力工程师快速掌握射频滤波器设计技巧。

weixin_30790841的博客 481

Golang 开发规范

分享一个全网最全的Go语言项目开发规范。

分享 Go、云原生、AI Infra 相关技术 2650

常见js手撕题及算法总结

设计一个lazyMan,实现以下功能: LazyMan(‘Tony’); // Hi I am Tony LazyMan(‘Tony’).sleep(10).eat(‘lunch’); // Hi I am Tony // 等待了10秒… // I am eating lunch LazyMan(‘Tony’).eat(‘lunch’).sleep(10).eat(‘dinner’); // H...

没有名字适合我的博客 3176

【Golang】进阶之路第一步:GO的编码规范

进阶之路第一步:GO的编码规范高质量在业内并没有一个十分标准的共识,它是一个偏主观的标准编写的代码能够达到正确可靠、简洁清晰的目标,就是高质量的代码在实际的业务场景中,经常并不是代码写的越高级就越好,各种边界条件是否考虑完备;异常情况处理;稳定性保证等细节更重要,在保证正确可靠的实现功能的前提下简洁清晰,可阅读性高的代码才是高质量的。GO语言开发者 Dave Cheney 的观点:消除“多余的复杂性”,以简单清晰的逻辑编写代码。 在实际开发中,对于一些复杂的逻辑,后来者基本上是不敢怎么动的。尤其是一些历史比

qq_45725675的博客 1145

深入解析ADC采样电容:从充电误差到LSB精度的关键设计

本文深入解析了ADC采样电容在模数转换中的核心作用,重点探讨了充电时间常数与采样误差的博弈关系。通过推导最小采样时间公式,揭示了精度、速度、源阻抗与内部电容之间的制约关系,并提供了从理论计算到外部驱动电路设计的实战指南,是确保ADC达到LSB精度的关键。

android的专栏 1103

跟着团子学SAP EPPM:项目管理常见的集成场景(PPM\PS\MS Project)

在SAP的EPPM解决方案中,针对企业战略级别的投资组合管理首先是定义合适的投资组合的层级结构,在投资组合下可以通过不同维度去定义投资篮(Portofolio Bucket)进行层层拆分,在最下层级的投资篮下可以分配明细的投资项(Portofolio Item),投资项可以是独立的一个一个具体执行的项目,也可以是单纯的一次服务等等。 今天主要聚焦在投资项的维度展开讲一下在SAP中投资项常见的几种集成模式: 集成场景1:使用PPM中的Project Management进行项目管理 第一个场景是在创建投资项

Lucas1211的博客 4139

golang编码规范

Go语言,也被称为Golang,是一种静态类型、编译型语言,由Google开发并在2009年发布。Go语言的设计目标是“解决大规模软件开发的问题”,并提供了一种简洁、高效和安全的方式来编写代码。以上就是关于Golang编码规范一些主要点,希望对你有所帮助。

高性价比服务器就选:蓝易云 486

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

【golang】Go编码规范指南

工作需要从Java转到Go上来了,再Java对象的世界里呆久了,Go的世界初来乍到雨点懵,希望能从标准库中整理出一点规范。 1.项目名 go项目名尽量小写加任何符号 2.package名 使用有意义的小写英文字母,尽量不要和标准库重复 3.变量 变量声明使用驼峰命名,不要使用“_”,多个命名放在一起 4.注释 行注释是常态,块注释一般用来包注释或者禁用大量代码 5.struct struct的变量...

WILL 945

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Go编码规范指南

参考:https://studygolang.com/articles/2059 Go编码规范指南 序言 看过很多方面的编码规范,可能每一家公司都有不同的规范,这份编码规范是写给我自己的,同时希望我们公司内部同事也能遵循这个规范来写Go代码。 如果你的代码没有办法找到下面的规范,那么就遵循标准库的规范,多阅读标准库的源码,标准库的代码可以说是我们写代码参考的标杆。 格式化规范 go默认已经有了gofmt工具,但是我们强烈建议使用goimport工具,这个在gofmt的基础上增加了自动删除和引入包. go g

小白的一次小程序编程的各种记录 1432

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

golang语言编码规范

本规范旨在为日常Go项目开发提供一个代码的规范指导,方便团队形成一个统一的代码风格,提高代码的可读性,规范性和统一性。本规范将从命名规范,注释规范,代码风格和 Go 语言提供的常用的工具这几个方面做一个说明。该规范参考了 go 语言官方代码的风格制定。 一、 命名规范 命名是代码规范中很重要的一部分,统一的命名规则有利于提高的代码的可读性,好的命名仅仅通过命名就可以获取到足够多的信息。 Go在命名...

qfliweimin的博客 976

Go编码规范

Go官方编码规范 命名 对于文件名,应一律小写,不同单词之间用下划线分隔 对于变量,采用驼峰命名法。专有名词大小写要一致,例:可以 URL 或 url ,而不要 Url 。变量名应该尽可能短,尤其是局部变量。对于一些特殊的变量以及全局变量,可能需要对它有更多的描述,可以使用长命名 对于interface,名字应以er结尾,优先使用标准库中已经存在的接口 对于结构体,注意字段的导出性,首字母大写的字段可导出 Getter: obj.Xxx() Setter: obj.SetXxx(xxx type) 对于接

AcTarjan的博客 567

Uber Go 语言编码规范

Uber Go 语言编码规范学习和笔记整理,超详细分析

2301_81125077的博客 1010

Go 语言编程 — 编码规范指南

目录 文章目录目录一个项目使用单个 GOPATHimport 规范代码风格 一个项目使用单个 GOPATH GOPATH 指定了 Golang 项目的 Workspace,Golang 是支持多 GOPATH 的,也就是说:在同一个 Golang 项目中可以同时拥有多个运行环境。多 GOPATH 支持带来了一定的灵活度,但也会导致某些副作用,例如:软件版本的一致性。 诸如 Etcd 或 Camlistore 这样的大项目通常会使用 godep 类似的依赖包管理工具,将所有依赖都保存到某个目录中。也就是说,这

烟云的计算 3794

golang编码规范参考

格式化规范 go默认已经有了gofmt工具,但是我们强烈建议使用goimport工具,这个在gofmt的基础上增加了自动删除和引入包. go get golang.org/x/tools/cmd/goimports 不同的编辑器有不同的配置, sublime的配置教程:http://michaelwhatcott.com/gosublime-goimports/ LiteIDE默认已经支持了goimports,如果你的不支持请点击属性配置->golangfmt->勾选goimports 保存之前

paul的专栏 352

Go 语言编码规范

uber-go/guide 的中文翻 Uber Go 语言编码规范 Uber 是一家美国硅谷的科技公司,也是 Go 语言的早期 adopter。其开源了很多 golang 项目,诸如被 Gopher 圈熟知的 zap、jaeger 等。2018 年年末 Uber 将内部的 Go 风格规范 开源到 GitHub,经过一年的积累和更新,该规范已经初具规模,并受到广大 Gopher 的关注。本文是该规范的中文版本。本版本会根据原版实时更新。 版本 当前更新版本:2021-04-23 版本地址:commit:#1

静水 712

【Golang语言编码规范】

go语言编码规范

ITCBL 684

Go的编码规范

vet工具可以帮我们静态分析我们的源码存在的各种问题,例如多余的代码,提前return的逻辑,struct的tag是否符合标准等。

m0_63230155的博客 340
上一篇: golang 结构体作为map的元素时,不能够直接赋值给结构体的某个字段
下一篇: Mac 常用软件
Webben
博客等级 码龄10年 19粉丝 98原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值