Skip to content

代码风格

字数
1617 字
阅读时间
7 分钟

官方提供的标准参考意见:

1. 命名风格

1.1 包名(Package)

  • 格式:简短、全小写、无下划线,多词连写,合理缩写(如 tabwriter 而非 tab_writerTabWriter)。
  • 避免阴影变量:包名不应与常用局部变量名冲突。
  • 导入重命名:第三方或生成包若含下划线,导入时需重命名(如 import foopb "path/to/foo_go_proto")。
  • 避免无意义名称:如 utilcommon,缺乏功能指向,易积累无关内容,导致依赖膨胀和导入冲突。

1.2 常量(Constant)

  • 格式:使用 MixedCaps,导出常量首字母大写,不导出则小写。

1.3 变量(Variable)

  • 作用域相关:小作用域可用单字母(如循环 i),大作用域需描述性名称(如 userCount)。
  • 避免类型后缀userCount 优于 numUsersusersInt
  • 简洁性:利用上下文省略冗余词(如方法 UserCount() 内的变量可用 count 而非 userCount)。
  • 避免名称重复:使用包的时候通过包名前缀区分内容,包内成员无需重复包名(http.Server 而不是 http.HTTPServer。)

1.4 接受者名字(Receiver)

  • 要求:简短(1-2 字母)、类型缩写、一致性。
  • 示例
    原名称推荐名称
    func (tray Tray)func (t Tray)
    func (info *ResearchInfo)func (ri *ResearchInfo)
    func (this *ReportWriter)func (w *ReportWriter)

1.5 首字母缩写(Initialisms)

  • 一致性:命名中的首字母缩写需要保留一致大小写,如 XMLAPI 而非 XMLapiXmlApi
  • 特例:包含一个小写字母,则保留原样,如 DDoS 而非 DDOS

1.6 获取函数(Getters)

  • 避免前缀:不使用 Get/get 前缀,直接用名词(如 Counts() 优于 GetCounts())。
  • 增加暗示:远程调用或耗时操作可用 FetchCompute 等代表执行是耗时间的,可能阻塞或失败。

2. import

2.1 分组

  • 标准库与第三方/项目包分开,空行分隔。
    go
    import (
        "fmt"
        "os"
    
        "github.com/dsnet/compress/flate"
        "myproj/foo/proto"
    )

2.2 空白导入

  • 仅在 main 包或测试中导入仅用于副作用的包(import _ "pkg")。

2.3 import 的点

  • 仅在因循环依赖无法属于被测试包的测试中使用 import . "foo",其他场景禁止(影响可读性)。
    go
    // 改为 foo_test 而不是 package foo,
    pacakge foo_test
    
    import (
    // 通过这种方式也可以直接使用 foo 包的导出变量、函数。
    . "foo" 
    )

3. 注释

3.1 导出对象

  • 所有导出的类型、函数必须有注释,以对象名开头,完整句子。
    go
    // Request represents a request to run a command.
    type Request struct { ... }
    
    // Encode writes the JSON encoding of req to w.
    func Encode(w io.Writer, req *Request) { ... }

3.2 结构体字段

  • 注释可紧跟字段,描述用途(如 BaseDir string // 存储莎士比亚作品的基础目录)。

3.3 包注释

  • 紧邻 package 声明,无空行,单个包注释覆盖整个包。 main 包的注释以命令行或二进制文件名开头。

    go
    // Package foo ...
    package foo
    go
    // Command xxx ...
    package main

4. 控制结构 & 函数 & 方法

4.1 裸返回(Naked Returns)

  • 小规模函数:允许简短函数使用裸返回(无参数的 return)。
    go
    func split(sum int) (x, y int) {
        x = sum * 4 / 9
        y = sum - x
        return // 裸返回,命名结果参数
    }
  • 中大规模函数:显式返回值,避免命名结果参数导致的冗余和可读性问题。

4.2 非必要不传指针

  • 避免指针:避免在函数中传递指针参数,除非需要修改传入参数。

4.3 接受者的类型

  • 引用类型的特殊处理:
    若接收者是 mapfunc chan,无需使用指针(本身已是引用类型)。若接收者是 slice 且方法不涉及重新切片或重新分配,也无需使用指针。

  • 小且不变的类型:
    对于小的、不可变的结构体(如 time.Time)、数组或基本类型(intstring),值接收者是合理选择(可能减少堆内存分配,降低垃圾回收压力)。

  • 需修改接收者时:
    若方法需要修改接收者,必须使用指针接收者(值接收者是原变量的拷贝,修改不会影响原变量)。

  • 含同步字段的结构体:
    若结构体接收者包含如 sync.Mutex 这类同步字段,必须使用指针接收者,以避免因拷贝导致同步状态混乱。

  • 大对象的性能考量:
    对于大的结构体或数组,指针接收者更高效。

  • 元素可变性暗示:
    若接收者(结构体、数组、切片)的元素包含指针,优先使用指针接收者,表明里面的内容可以被修改。

  • 保持一致性: 避免混合使用接收者类型,所有方法应保持一致(要么全部使用指针接收者,要么全部使用值接收者)。

  • 拿不准时选指针:
    当不确定如何选择时,优先使用指针接收者。

4.4 JSON序列化

  • struct 字段需首字母大写并使用 json tag。
    go
    type User struct {
        FirstName string `json:"first_name"` // 字段首字母大写,json tag 下划线或小驼峰(统一即可)
        Age  int    `json:"age"` 
    }

4.5 接口设计(Interfaces)

  • 定义位置:接口应在消费方包中定义,实现方返回具体类型(如 consumer.Thinger 接口在消费方包中声明)。
  • 避免提前定义:仅在实际使用时定义接口
  • 测试友好:通过具体类型实现接口,便于消费方模拟(Mock)而不依赖实现方接口。

贡献者

页面历史


总访问量 次, 访客数 人次