- 代码经由gofmt或goimports工具格式化
- 代码已通过go tool vet、golint、gometalinter等工具的检查
不要使用相对路径,使用绝对路径。
import ../config
import github.com/voidint/gbb/config
func run() (n int, err error) {
// ...
return
}
func run() (n int, err error) {
// ...
return n, err
}
需要使用整形数据类型情况下,多数都可以直接使用int
,而不建议使用uint
。标准库及其他第三方的类库,在类似的使用场景下多使用int
。那么,此种随大流
可以免去不必要的数据类型转换。
type Person struct{
Age int
}
type Person struct{
Age uint
}
空slice的声明
s := []string{} // 声明并初始化变量
var s []string // 声明
函数内的变量声明,推荐使用:=
的简短赋值语句。
func saySomething() {
var msg = getMsg()
fmt.Println(msg)
}
func saySomething() {
msg := getMsg()
fmt.Println(msg)
}
函数、方法、结构体、结构体属性等导出与否,由使用场景决定,但秉承着权限最小化
的原则,能不导出就不要导出。
type Student struct{
Name string
}
stu := Student{
Name: "voidint",
}
func main(){
fmt.Printf("student name: %s\n", stu.Name)
}
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())
}
使用驼峰式
的变量命名方式。
var student_name string
const LOG_LEVEL = "info"
var studentName string
const LogLevel = "info"
专有名词的缩写应保持字母大小写一致。具体是全部大写还是全部小写,应由实际使用场景决定。若变量可以不导出,那就全部小写,反之全部大写。
var (
Cpu string
Id int
)
var (
CPU string
id int
)
形参变量和函数返回值变量一律小写字母开头。
func add(X, Y int) (Z int) {
return X + Y
}
func add(x, y int) (z int) {
return x + y
}
为函数的多个返回值命名。在godoc
生成的文档中,带有返回值的函数声明更利于理解。
func SplitHostPort(hostport string) (string, string, error){
...
}
func SplitHostPort(hostport string) (host, port string, err error){
...
}
建议:每当变量命名不知所措时,使用codelf看看世界上其他程序员的命名。
包名由小写字母组成,不要使用下划线或者混合大小写。
package busyBox
package busybox
包名不要使用复数
package utils
package util
所有对包内的引用都应该使用包名去访问,因此包内的名称引用可以去掉包名这个标识。
package chubby
type ChubbyFile struct{
}
package chubby
type File struct{
}
理想情况下,接口命名以er
结尾。
type IRead interface{
}
type Reader interface{
}
遵循简洁的原则,对于包内的源代码文件命名,一般情况下无需再携带包信息。
// service/network_service.go
package service
// service/network.go
package service
一般情况下,都需要对函数的返回值error进行逻辑判断。
如果函数返回的错误确实可以不处理,那么请用_
明确告知他人并非是忘记处理error而是将error丢弃
。丢弃error,本身也是一种处理方式。
scores := map[string]int{
"jim": 8,
"jerry": 7,
"tom": 3,
}
b,_ := json.Marshal(scores) // TODO 换个更好的例子
fmt.Printf("%s", b)
仅能使用英文系字符且以小写字母开头
var (
ErrInvalidIP = errors.New("无效的IP")
ErrInvalidMacAddr = errors.New("Invalid mac address")
)
var (
ErrInvalidIP = errors.New("invalid ip")
ErrInvalidMacAddr = errors.New("invalid mac address")
)
不要以标点符号结尾
var (
ErrInvalidIP = errors.New("invalid ip!")
)
var (
ErrInvalidIP = errors.New("invalid ip")
)
在一般业务代码中,建议使用error
而非panic
。
func (repo model.Repo) GetUserByName(name string)(user *model.User){
if name == ""{
panic("user name can't be empty")
}
...
}
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
开头,并且在注释中明确告知此函数的副作用
。
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
。
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")
}
- 对于
error
、panic
模棱两可的情况,就选error
。
多个字符串拼接时不要使用+
,建议使用fmt.Sprintf
或者strings.Builder
。
func (stu Student) String() string {
return stu.Num + " " + stu.Name + " " + stu.Age
}
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是map,func,chan,不使用指针。
如果receiver是slice,当方法不会重组或重新分配切片,不使用指针。
如果方法需要改变receiver,必须使用指针。
当receiver是包含锁或同步字段时,必须使用指针以避免复制。
对于大的结构体或数组,指针更加的高效。
当外面的改动必须影响到原始的receiver时,必须使用指针。
最后,如果怀疑,那么请使用指针。
将变量作用域
控制在最小范围内。
err := file.Chmod(0664)
if err != nil {
return err
}
if err := file.Chmod(0664); err != nil {
return err
}
应尽早return结束代码块。尽量不要将语句嵌入代码块中,以避免层次过深,增加复杂度。
func gender(female bool) (desc string){
if female {
return "female"
} else {
return "male"
}
}
func gender(female bool) (desc string){
if female {
return "female"
}
return "male"
}
判断结构中比较布尔类型变量
func gender(female bool) (desc string){
if female == true {
return "female"
}
return "male"
}
func gender(female bool) (desc string){
if female{
return "female"
}
return "male"
}
遍历slice时应尽量使用下标访问元素,避免不必要的内存拷贝。
type BigStruct struct{
ID int
}
items := []BigStruct{
{ID: 0},
{ID: 1},
}
for _, item := range items{ // 每次都会将下标元素拷贝至变量item
fmt.Println(item.ID)
}
type BigStruct struct{
ID int
}
items := []BigStruct{
{ID: 0},
{ID: 1},
}
for i := range items{ // 通过下标访问slice中的元素,无拷贝
fmt.Println(items[i].ID)
}