Golang代碼規範

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

Golang代碼規範

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

項目目錄結構規範

PROJECT_NAME├── README.md 介紹軟體及文檔入口├── bin 編譯好的二進位檔案,執行./build.sh自動產生,該目錄也用於程式打包├── build.sh 自動編譯的指令碼├── doc 該項目的文檔├── pack 打包後的程式放在此處├── pack.sh 自動打包的指令碼,產生類似xxxx.20170713_14:45:35.tar.gz的檔案,放在pack檔案下└── src 該項目的原始碼    ├── main 項目主函數    ├── model 項目代碼    ├── research 在實現該項目中探究的一些程式    └── vendor 存放go的庫        ├── github.com/xxx 第三方庫        └── xxx.com/obc 公司內部的公用庫

項目的目錄結構盡量做到簡明、層次清楚

檔案名稱命名規範

用小寫,盡量見名思義,看見檔案名稱就可以知道這個檔案下的大概內容,對於原始碼裡的檔案,檔案名稱要很好的代表了一個模組實現的功能。

命名規範

包名

包名用小寫,使用短命名,盡量和標準庫不要衝突

介面名

單個函數的介面名以”er”作為尾碼,如Reader,Writer

介面的實現則去掉“er”

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

兩個函數的介面名綜合兩個函數名

type WriteFlusher interface {    Write([]byte) (int, error)    Flush() error}

三個以上函數的介面名,類似於結構體名

type Car interface {    Start([]byte)     Stop() error    Recover()}

變數

全域變數:採用駝峰命名法,僅限在包內的全域變數,包外引用需要寫介面,提供調用局部變數:駝峰式,小寫字母開頭

常量

常量:大寫,採用底線

import 規範

import在多行的情況下,goimports會自動幫你格式化,在一個檔案裡面引入了一個package,建議採用如下格式:

import (    "fmt")

如果你的包引入了三種類型的包,標準庫包,程式內部包,第三方包,建議採用如下方式進行組織你的包:

import (    "encoding/json"    "strings"    "myproject/models"    "myproject/controller"    "git.obc.im/obc/utils"    "git.obc.im/dep/beego"    "git.obc.im/dep/mysql")  

在項目中不要使用相對路徑引入包:

// 這是不好的匯入

import “../net”

// 這是正確的做法

import “xxxx.com/proj/net”

函數名

函數名採用駝峰命名法,盡量不要使用底線

錯誤處理

error作為函數的值返回,必須儘快對error進行處理
採用獨立的錯誤流進行處理
不要採用這種方式

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

而要採用下面的方式

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

如果傳回值需要初始化,則採用下面的方式

x, err := f()if err != nil {    // error handling    return}// use x

Panic

在邏輯處理中禁用panic
在main包中只有當實在不可啟動並執行情況採用panic,例如檔案無法開啟,資料庫無法串連導致程式無法 正常運行,但是對於其他的package對外的介面不能有panic,只能在包內採用。建議在main包中使用log.Fatal來記錄錯誤,這樣就可以由log來結束程式。

Recover

recover用於捕獲runtime的異常,禁止濫用recover,在開發測試階段盡量不要用recover,recover一般放在你認為會有不可預期的異常的地方。

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 函數可能會有不可預期的異常    do(work)}

Defer

defer在函數return之前執行,對於一些資源的回收用defer是好的,但也禁止濫用defer,defer是需要消耗效能的,所以頻繁調用的函數盡量不要使用defer。

// Contents returns the file's contents as a string.func Contents(filename string) (string, error) {    f, err := os.Open(filename)    if err != nil {        return "", err    }    defer f.Close()  // f.Close will run when we're finished.    var result []byte    buf := make([]byte, 100)    for {        n, err := f.Read(buf[0:])        result = append(result, buf[0:n]...) // append is discussed later.        if err != nil {            if err == io.EOF {                break            }            return "", err  // f will be closed if we return here.        }    }    return string(result), nil // f will be closed if we return here.}

控制結構

if

if接受初始化語句,約定如下方式建立局部變數

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

for

採用短聲明建立局部變數

sum := 0for i := 0; i < 10; i++ {    sum += i}

range

如果只需要第一項(key),就丟棄第二個:

for key := range m {    if key.expired() {        delete(m, key)    }}

如果只需要第二項,則把第一項置為底線

sum := 0for _, value := range array {    sum += value}

return

儘早return:一旦有錯誤發生,馬上返回

f, err := os.Open(name)if err != nil {    return err}d, err := f.Stat()if err != nil {    f.Close()    return err}codeUsing(f, d)

方法的接收器

名稱一般採用strcut的第一個字母且為小寫,而不是this,me或者self

    type T struct{}     func (p *T)Get(){}

如果接收者是map,slice或者chan,不要用指標傳遞

//Mappackage mainimport (    "fmt")type mp map[string]stringfunc (m mp) Set(k, v string) {    m[k] = v}func main() {    m := make(mp)    m.Set("k", "v")    fmt.Println(m)}
//Channelpackage mainimport (    "fmt")type ch chan interface{}func (c ch) Push(i interface{}) {    c <- i}func (c ch) Pop() interface{} {    return <-c}func main() {    c := make(ch, 1)    c.Push("i")    fmt.Println(c.Pop())}

如果需要對slice進行修改,通過傳回值的方式重新賦值

//Slicepackage mainimport (    "fmt")type slice []bytefunc main() {    s := make(slice, 0)    s = s.addOne(42)    fmt.Println(s)}func (s slice) addOne(b byte) []byte {    return append(s, b)}

如果接收者是含有sync.Mutex或者類似同步欄位的結構體,必須使用指標傳遞避免複製

package mainimport (    "sync")type T struct {    m sync.Mutex}func (t *T) lock() {    t.m.Lock()}/*Wrong !!!func (t T) lock() {    t.m.Lock()}*/func main() {    t := new(T)    t.lock()}

如果接收者是大的結構體或者數組,使用指標傳遞會更有效率。

package mainimport (    "fmt")type T struct {    data [1024]byte}func (t *T) Get() byte {    return t.data[0]}func main() {    t := new(T)    fmt.Println(t.Get())}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.