Skip to content

voidint/go-style-guide

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

7 Commits
 
 

Repository files navigation

Go代码规范(beta)

目录

前提

代码规范

import

不要使用相对路径,使用绝对路径。

Don't
import ../config
Do
import github.com/voidint/gbb/config

return

Don't
func run() (n int, err error) {
	// ...
	return
}
Do
func run() (n int, err error) {
	// ...
	return n, err
}

不建议使用uint

需要使用整形数据类型情况下,多数都可以直接使用int,而不建议使用uint。标准库及其他第三方的类库,在类似的使用场景下多使用int。那么,此种随大流可以免去不必要的数据类型转换。

Do
type Person struct{
	Age int
}
Don't
type Person struct{
	Age uint
}

变量声明

空slice的声明

Don't
s := []string{} // 声明并初始化变量
Do
var s []string // 声明

函数内的变量声明,推荐使用:=的简短赋值语句。

Don't
func saySomething() {
	var msg = getMsg()
	fmt.Println(msg)
}
Do
func saySomething() {
	msg := getMsg()
	fmt.Println(msg)
}

变量导出

函数、方法、结构体、结构体属性等导出与否,由使用场景决定,但秉承着权限最小化的原则,能不导出就不要导出。

Don't
type Student struct{
	Name string
}

stu := Student{
	Name: "voidint",
}

func main(){
	fmt.Printf("student name: %s\n", stu.Name)
}
Do
type Student struct{
	name string
}

func NewStudent(name string) *Student{
	return &Student{name: name}
}

func (stu Student) Name() string{
	return stu.name
}

func main(){
	fmt.Printf("student name: %s\n", NewStudent("voidint").Name())
}

变量命名

使用驼峰式的变量命名方式。

Don't
var	student_name string
const LOG_LEVEL = "info"
Do
var	studentName string
const LogLevel = "info"

专有名词的缩写应保持字母大小写一致。具体是全部大写还是全部小写,应由实际使用场景决定。若变量可以不导出,那就全部小写,反之全部大写。

Don't
var (
	Cpu string 
	Id int
)
Do
var (
	CPU string 
	id int
)

形参变量和函数返回值变量一律小写字母开头。

Don't
func add(X, Y int) (Z int) {
	return X + Y
}
Do
func add(x, y int) (z int) {
	return x + y
}

为函数的多个返回值命名。在godoc生成的文档中,带有返回值的函数声明更利于理解。

Don't
func SplitHostPort(hostport string) (string, string, error){
...
}
Do
func SplitHostPort(hostport string) (host, port string, err error){
...
}

建议:每当变量命名不知所措时,使用codelf看看世界上其他程序员的命名。

package命名

包名由小写字母组成,不要使用下划线或者混合大小写。

Don't
package busyBox
Do
package busybox

包名不要使用复数

Don't
package utils
Do
package util

所有对包内的引用都应该使用包名去访问,因此包内的名称引用可以去掉包名这个标识。

Don't
package chubby

type ChubbyFile struct{
}
Do
package chubby

type File struct{
}

interface命名

理想情况下,接口命名以er结尾。

Don't
type IRead interface{
}
Do
type Reader interface{
}

源代码文件命名

遵循简洁的原则,对于包内的源代码文件命名,一般情况下无需再携带包信息。

Don't
// service/network_service.go
package service
Do
// service/network.go
package service

error处理

一般情况下,都需要对函数的返回值error进行逻辑判断。

如果函数返回的错误确实可以不处理,那么请用_明确告知他人并非是忘记处理error而是将error丢弃。丢弃error,本身也是一种处理方式。

Don't
Do
scores := map[string]int{
	"jim": 8,
	"jerry": 7,
	"tom": 3,
}

b,_ := json.Marshal(scores) // TODO 换个更好的例子
fmt.Printf("%s", b)

error文本内容

仅能使用英文系字符且以小写字母开头

Don't
var (
	ErrInvalidIP = errors.New("无效的IP")
	ErrInvalidMacAddr = errors.New("Invalid mac address")
)
Do
var (
	ErrInvalidIP = errors.New("invalid ip")
	ErrInvalidMacAddr = errors.New("invalid mac address")
)

不要以标点符号结尾

Don't
var (
	ErrInvalidIP = errors.New("invalid ip!")
)
Do
var (
	ErrInvalidIP = errors.New("invalid ip")
)

panic与error

在一般业务代码中,建议使用error而非panic

Don't
func (repo model.Repo) GetUserByName(name string)(user *model.User){
	if name == ""{
		panic("user name can't be empty")
	}
	...
}
Do
var ErrEmptyUserName = errors.New("user name can't be empty")

func (repo model.Repo) GetUserByName(name string)(user *model.User, err error){
	if name == ""{
		return nil, ErrEmptyUserName
	}
	...
}

有时候为了降低函数的错误处理成本,可以把函数的error返回值移除。而函数一旦发生错误,则发生panic。这种场景要求函数名称以Must开头,并且在注释中明确告知此函数的副作用

Do
package regexp

// MustCompile is like Compile but panics if the expression cannot be parsed.
// It simplifies safe initialization of global variables holding compiled regular
// expressions.
func MustCompile(str string) *Regexp {
  	regexp, error := Compile(str)
  	if error != nil {
  		panic(`regexp: Compile(` + quote(str) + `): ` + error.Error())
  	}
  	return regexp
}

对于那些可以被认为是绝对不可能发生的事,可谨慎使用panic

Do
type Season int

const (
	Spring Season = iota
	Summer
	Fall
	Winter
)

func SeasonName(season Season) string {
	switch season {
	case Spring:
		return "Spring"
	case Summer:
		return "Summer"
	case Fall:
		return "Fall"
	case Winter:
		return "Winter"
	}
	panic("unreachable") 
}
  • 对于errorpanic模棱两可的情况,就选error

字符串拼接

多个字符串拼接时不要使用+,建议使用fmt.Sprintf或者strings.Builder

Don't
func (stu Student) String() string {
	return stu.Num + " " + stu.Name + " " + stu.Age
}
Do
func (stu Student) String() string {
	return fmt.Sprintf("%s %s %d", stu.Num, stu.Name, stu.Age)
}

// OR

func (stu Student) String() string {
	var buf bytes.Buffer
	buf.WriteString(stu.Num)
	buf.WriteString(" ")
	buf.WriteString(stu.Name)
	buf.WriteString(" ")
	buf.WriteString(stu.Age)
	return buf.String()
}

receiver类型

如果receiver是map,func,chan,不使用指针。

如果receiver是slice,当方法不会重组或重新分配切片,不使用指针。

如果方法需要改变receiver,必须使用指针。

当receiver是包含锁或同步字段时,必须使用指针以避免复制。

对于大的结构体或数组,指针更加的高效。

当外面的改动必须影响到原始的receiver时,必须使用指针。

最后,如果怀疑,那么请使用指针。

控制结构

变量作用域控制在最小范围内。

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

应尽早return结束代码块。尽量不要将语句嵌入代码块中,以避免层次过深,增加复杂度。

Don't
func gender(female bool) (desc string){
	if female {
		return "female"
	} else {
		return "male"
	}
}
Do
func gender(female bool) (desc string){
	if female {
		return "female"
	}
	return "male"
}

判断结构中比较布尔类型变量

Don't
func gender(female bool) (desc string){
	if female == true {
		return "female"
	}
	return "male"
}
Do
func gender(female bool) (desc string){
	if female{
		return "female"
	}
	return "male"
}

遍历slice时应尽量使用下标访问元素,避免不必要的内存拷贝。

Don't
type BigStruct struct{
	ID int
}

items := []BigStruct{
	{ID: 0},
	{ID: 1},
}

for _, item := range items{ // 每次都会将下标元素拷贝至变量item
	fmt.Println(item.ID)
}
Do
type BigStruct struct{
	ID int
}

items := []BigStruct{
	{ID: 0},
	{ID: 1},
}

for i := range items{ // 通过下标访问slice中的元素,无拷贝
	fmt.Println(items[i].ID)
}

参考

Releases

No releases published

Packages

No packages published