自己易忽略的golang规范

  • Interface 合理性验证

var _ http.Handler = (*Handler)(nil)
  • 关于Mutex

type smap struct {
  sync.Mutex // only for unexported types(仅适用于非导出类型)

  data map[string]string
}

type SMap struct {
  mu sync.Mutex // 对于导出类型,请使用私有锁

  data map[string]string
}
  • Channel 的 size 要么是 1,要么是无缓冲的

    // 大小:1
    c := make(chan int, 1) // 或者
    // 无缓冲 channel,大小为 0
    c := make(chan int)
    
  • 枚举从 1 开始

    type Operation int
    
    const (
      Add Operation = iota + 1
      Subtract
      Multiply
    )
    
    // Add=1, Subtract=2, Multiply=3
    

    在某些情况下,使用零值是有意义的(枚举从零开始),例如,当零值是理想的默认行为时。

  • Errors返回形式

    • 这是一个不需要额外信息的简单错误吗?如果是这样,errors.New 足够了。

    • 客户需要检测并处理此错误吗?如果是这样,则应使用自定义类型并实现该 Error() 方法。

    • 您是否正在传播下游函数返回的错误?如果是这样,请查看本文后面有关错误包装 section on error wrapping 部分的内容。

    • 否则 fmt.Errorf 就可以了。

      如果客户端需要检测错误,并且您已使用创建了一个简单的错误 errors.New,请使用一个错误变量。

      // package foo
      
      var ErrCouldNotOpen = errors.New("could not open")
      
      func Open() error {
        return ErrCouldNotOpen
      }
      
      // package bar
      
      if err := foo.Open(); err != nil {
        if err == foo.ErrCouldNotOpen {
          // handle
        } else {
          panic("unknown error")
        }
      }
      

      如果您有可能需要客户端检测的错误,并且想向其中添加更多信息(例如,它不是静态字符串),则应使用自定义类型。

      type errNotFound struct {
        file string
      }
      
      func (e errNotFound) Error() string {
        return fmt.Sprintf("file %q not found", e.file)
      }
      
      func open(file string) error {
        return errNotFound{file: file}
      }
      
      func use() {
        if err := open("testfile.txt"); err != nil {
          if _, ok := err.(errNotFound); ok {
            // handle
          } else {
            panic("unknown error")
          }
        }
      }
      

      直接导出自定义错误类型时要小心,因为它们已成为程序包公共 API 的一部分。最好公开匹配器功能以检查错误。

      // package foo
      
      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
      }
      
      func Open(file string) error {
        return errNotFound{file: file}
      }
      
      // package bar
      
      if err := foo.Open("foo"); err != nil {
        if foo.IsNotFoundError(err) {
          // handle
        } else {
          panic("unknown error")
        }
      }
      
  • Atomic

    使用 sync/atomic 包的原子操作对原始类型 (int32, int64等)进行操作,因为很容易忘记使用原子操作来读取或修改变量。

    go.uber.org/atomic 通过隐藏基础类型为这些操作增加了类型安全性。此外,它包括一个方便的atomic.Bool类型。

    type foo struct {
      running atomic.Bool
    }
    
    func (f *foo) start() {
      if f.running.Swap(true) {
         // already running…
         return
      }
      // start the Foo
    }
    
    func (f *foo) isRunning() bool {
      return f.running.Load()
    }
    
  • Slice

    要检查切片是否为空,请始终使用len(s) == 0。而非 nil

    零值切片(用var声明的切片)可立即使用,无需调用make()创建。

    记住,虽然nil切片是有效的切片,但它不等于长度为0的切片(一个为nil,另一个不是),并且在不同的情况下(例如序列化),这两个切片的处理方式可能不同。

你可能感兴趣的:(go)