Go 编写姿势

  • 一般情况下,interface 可以直接进行值传递,除非你需要修改 interface 指向的数据。interface 本身很轻量,其包括指向数据类型的指针和存储数据的指针。
  • Value Receiver 方法可以通过值或者指针调用;Pointer Receiver 则只接受指针调用。换句话说,指针调用方法更加轻松,限制更少。
  • Mutex 和 RWMutex 可以直接使用其零值,零值表示 Unlock 状态。已经被使用的 Mutex 不能被拷贝。
  • 对于 Map & Slice,需要注意其作为参数和返回值的时候,可能会受到外界操作的影响。 安全的做法是在内部做一次拷贝,内部可安全使用。

    1
    2
    d.trips = make([]Trip, len(trips))
    copy(d.trips, trips)
  • Channel 的 size 要么是 1,要么是无缓冲的(这个还是要看情况吧)

  • 常规情况下,枚举从 1 开始计数;除非 0 是有一定意义的。如:LogToStdout = 0
  • 直接导出自定义错误要小心,最好只公开错误匹配器,方便进行错误检查:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    type errNotFound struct {
    file string
    }

    func (e errNotFound) Error() string {
    return fmt.Sprintf("file %q not found", e.file)
    }

    func IsNotFoundError(err error) bool {
    _, ok := err.(errNotFound)
    return ok
    }
  • 合理使用 Error Wrapping,不用添加过多冗余信息,如:failed to : error message

  • 在生产环境中,运行的程序避免 panic,panic/recover 不是错误处理策略,而是当不可恢复的事情发生时,程序才必须 panic。
  • 使用 go.uber.org/atomic 替代标准库 sync/atomic,避免忘记使用原子操作。
  • import _ 应该只用于 main 文件中,尽可能靠近程序启动位置。

性能

  • 优先使用 strconv 而非 fmt 转换类型,性能更好。
  • 不要反复(如循环中)从固定字符串创建字节 slice,反之亦然。
  • map 创建时尽量提供容量 hint,这样可以避免在添加元素期间过多次地分配。map 虽然不能保证分配 hint 个容量,添加元素时依然可以进行分配,但它可以在运行时有更少的分配。

规范

  1. 保证一致性,便于代码的维护、减少学习成本
  2. 相似的声明放在一个分组(import/var/const/type)。仅将相关的声明放到一起!
  3. 包命名规则:
    1. 全部小写。无大写或下划线
    2. 多数使用命名导入的情况下,无需重命名包
    3. 简短 & 简洁
    4. 不要使用复数
  4. 函数分组与顺序:
    1. 函数粗略按照调用顺序排序
    2. 同一个文件中,函数应该按照接收者分组
    3. 导出的函数应该先出现在文件中,放在 var, struct, const 定义的后面
1
2
3
4
5
6
7
8
9
type something struct{ ... }
func newSomething() *something {
return &something{}
}
func (s *something) Cost() {
return calcCost(s.weights)
}
func (s *something) Stop() {...}
func calcCost(n []int) int {...}
  • 对于未导出的顶层常量和变量,使用 _ 作为前缀。未导出的错误值,使用 err 开头。原因:顶层变量和常量具有包范围作用域,使用通用名称可能会导致在其他文件中意外使用错误值。
  • 结构体嵌入,多一行空行隔开
  • nil 是一个有效的 slice;零值切片(使用 var 声明的切片)可立即使用,无需调用 make() 创建。

    1
    2
    3
    4
    5
    6
    7
    var nums []int
    if add1 {
    nums = append(nums, 1)
    }
    if add2 {
    nums = append(nums, 2)
    }
  • 如果要声明格式字符串,设置为 const 类型,有助于 go vet 执行字符串静态检查:

    1
    2
    const msg = "unexpected values %v, %v\n"
    fmt.Printf(msg, 1, 2)

更多参考

0%