Go 语言的一些编程规范

专职跑龙套 · · 786 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

主要参考了 Google 对外发布的编程规范:Go Code Review Comments 和内部的一些使用习惯。

两个小工具

  • gofmt:帮助你自动格式化代码
  • goimports:帮助你自动添加/删除包的引用

代码注释的语句

更多参见:https://golang.org/doc/effective_go.html#commentary
尽管有时候会显得比较多余,但是代码注释最好是完整的语句,并且用句号结尾。
这样做的好处是,在自动生成 godoc 文档时,会产生更好的格式。

Contexts

如果需要 Contexts 的话,将它作为方法的第一个参数传入:
func F(ctx context.Context, /* other arguments */) {}

  • 不要将 Context 作为一个结构体成员变量
  • 不要自定义 Context 类型
  • Context 是一个 immutable 不可变对象

复制 Copying

例如,bytes.Buffer 类型包含一个 []byte 切片。当你去复制一个 Buffer 对象时,实际最后都是指向同一个 []byte

定义空的切片 Declaring Empty Slices

推崇的方式 var t []string:定义了个 nil
不推崇的方式 t := []string{}:不是 nil,但是长度为 0
不过对上面这两种方式定义的变量,调用 len()cap() 的结果都是 0

加密随机数 Crypto Rand

Do not use package math/rand to generate keys, even throwaway ones. Unseeded, the generator is completely predictable. Seeded with time.Nanoseconds(), there are just a few bits of entropy. Instead, use crypto/rand's Reader, and if you need text, print to hexadecimal or base64:
不要使用 math/rand 来产生加密的 keys,因为 math/rand 的随机数算法是可预测的。
推荐使用 crypto/rand,也可以将结果转换成 hexadecimal 或者 base64
示例:

import (
    "fmt"
    "crypto/rand"
)

func Key() string {
    buf := make([]byte, 16)
    _, err := rand.Read(buf)
    if err != nil {
        panic(err)  // out of randomness, should never happen
    }
    return fmt.Sprintf("%x", buf)
    // or hex.EncodeToString(buf)
    // or base64.StdEncoding.EncodeToString(buf)
}

func main() {
    fmt.Println(Key()) // 40ca8b6ff7e65501b097cc0e9aebdc2e
    fmt.Println(Key()) // 5faaad1a34483977420349e980954b6f
}

文档注释 Doc Comments

Exported 导出的方法/变量需要有注释。

不要使用 Panic Don't Panic

不要使用 Panic 来进行异常的处理,例如:

import (
    "os"
)

func main() {
    _, err := os.Create("/tmp/file")
        if err != nil {
            panic(err) // 不推荐
        }
}

异常语句 Error Strings

异常语句不要以大写开头,结尾也不要有标点符号
推荐:fmt.Errorf("something bad")
不推荐:fmt.Errorf("Something bad")
什么原因呢?因为通常异常都会被捕获,最后作为日志的一部分打印出来,所以就最好不要有大写字母和标点符号。

因此对于日志语句而言,可以大写开头,也可以标点符号结尾,例如 log.Printf("Reading %s: %v", filename, err)

示例 Examples

在添加新的 package 时,可以添加一些示例,会自动添加到 godoc 文档中,例如:


示例 Examples

Goroutine Lifetimes 生命周期

使用 goroutines 时候,确保他们及时退出。

处理异常 Handle Errors

不要使用 _ 来忽略异常,例如 res, _ := func()
要检查并处理异常,例如:

res, err := func()
if err != nil {
    ......
}

包的导入 Imports

尽量不用重命名 package,包的引用最好进行分组,使用空白行分隔,例如:

import (
    "fmt"
    "hash/adler32"
    "os"

    "appengine/foo"
    "appengine/user"

    "github.com/foo/bar"
    "rsc.io/goversion/version"
)

ImportBlank

Go 语言要求导入的包必须在后续中使用,否则会报错。
如果想要避免这个错误,可以在包的前面加上下划线 _,例如 _ "net/http"

问题来了,如果一个包不被使用,那为什么要导入呢?
因为导入匿名包仅仅表示无法再访问其内的属性。但导入这个匿名包的时候,会进行一些初始化操作,例如 init(),如果这个初始化操作会影响当前包,那么这个匿名导入就是有意义的。
例如:

import (
  "database/sql"

  _ "github.com/lib/pq" // 我们需要的是这个包里面的 init() 方法
)

Import Dot

如果不想在访问包属性的时候加上包名,则导入的时候,可以为其设置特殊的别名:点 .,例如:

import (
    . "fmt"
)

func main() {
    Println()    // 无需包名,直接访问Println
}

In-Band Errors

不要返回 -1 或者空指针来代表出现了异常。Go 支持多个返回值,因此将异常或者状态作为一个单独的值返回,例如:

// Lookup returns the value for key or ok=false if there is no mapping for key.
func Lookup(key string) (value string, ok bool)

这样的话,我们就可以通过如下的方式来调用该方法:

value, ok := Lookup(key)
if !ok  {
    return fmt.Errorf("no value for %q", key)
}
return Parse(value)

Indent Error Flow

保持缩进尽量的少。
例如下面一段代码:

if err != nil {
    // error handling
} else {
    // normal code
}

我们可以修改为如下的方式,从而减少 normal code 的缩进:

if err != nil {
    // error handling
    return // or continue, etc.
}
// normal code

Initialisms

对于缩略词,保持统一的大小写。
推荐 URL,不推荐 Url
推荐 ServeHTTP,不推荐 ServeHttp

接口 Interfaces

Line Length

Mixed Caps

Named Result Parameters

Naked Returns

Package Comments

Package Names

Pass Values

Receiver Names

Receiver Type

Synchronous Functions

Useful Test Failures

Variable Names


有疑问加站长微信联系(非本文作者)

本文来自:简书

感谢作者:专职跑龙套

查看原文:Go 语言的一些编程规范

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

786 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传