关于Go你不得不知道的小技巧1

简介: 关于Go你不得不知道的小技巧

Go 箴言

不要通过共享内存进行通信,通过通信共享内存

并发不是并行

管道用于协调;互斥量(锁)用于同步

接口越大,抽象就越弱

利用好零值

空接口 interface{} 没有任何类型约束

Gofmt 的风格不是人们最喜欢的,但 gofmt 是每个人的最爱

允许一点点重复比引入一点点依赖更好

系统调用必须始终使用构建标记进行保护

必须始终使用构建标记保护 Cgo

Cgo 不是 Go

使用标准库的 unsafe 包,不能保证能如期运行

清晰比聪明更好

反射永远不清晰

错误是值

不要只检查错误,还要优雅地处理它们

设计架构,命名组件,(文档)记录细节

文档是供用户使用的

不要(在生产环境)使用 panic()


Go 之禅

每个 package 实现单一的目的

显式处理错误

尽早返回,而不是使用深嵌套

让调用者处理并发(带来的问题)

在启动一个 goroutine 时,需要知道何时它会停止

避免 package 级别的状态

简单很重要

编写测试以锁定 package API 的行为

如果你觉得慢,先编写 benchmark 来证明

适度是一种美德

可维护性


代码

使用 go fmt 格式化

让团队一起使用官方的 Go 格式工具,不要重新发明轮子。

尝试减少代码复杂度。 这将帮助所有人使代码易于阅读。


多个 if 语句可以折叠成 switch

// NOT BAD
if foo() {
    // ...
} else if bar == baz {
    // ...
} else {
    // ...
}
// BETTER
switch {
case foo():
    // ...
case bar == baz:
    // ...
default:
    // ...
}


chan struct{} 来传递信号, chan bool 表达的不够清楚

当你在结构中看到 chan bool 的定义时,有时不容易理解如何使用该值,例如:

type Service struct {
    deleteCh chan bool // what does this bool mean? 
}

但是我们可以将其改为明确的 chan struct {} 来使其更清楚:我们不在乎值(它始终是 struct {}),我们关心可能发生的事件,例如:

type Service struct {
    deleteCh chan struct{} // ok, if event than delete something.
}

30 * time.Secondtime.Duration(30) * time.Second 更好

你不需要将无类型的常量包装成类型,编译器会找出来。

另外最好将常量移到第一位:

// BAD
delay := time.Second * 60 * 24 * 60
// VERY BAD
delay := 60 * time.Second * 60 * 24
// GOOD
delay := 24 * 60 * 60 * time.Second

time.Duration 代替 int64 + 变量名

// BAD
var delayMillis int64 = 15000
// GOOD
var delay time.Duration = 15 * time.Second

按类型分组 const 声明,按逻辑和/或类型分组 var

// BAD
const (
    foo = 1
    bar = 2
    message = "warn message"
)
// MOSTLY BAD
const foo = 1
const bar = 2
const message = "warn message"
// GOOD
const (
    foo = 1
    bar = 2
)
const message = "warn message"

这个模式也适用于 var

  • ** 每个阻塞或者 IO 函数操作应该是可取消的或者至少是可超时的
  • ** 为整型常量值实现 Stringer 接口
  • ** 检查 defer 中的错误


  defer func() {
      err := ocp.Close()
      if err != nil {
          rerr = err
      }
  }()
  • ** 不要在 checkErr 函数中使用 panic()os.Exit()
  • ** 仅仅在很特殊情况下才使用 panic, 你必须要去处理 error
  • ** 不要给枚举使用别名,因为这打破了类型安全
  package main
  type Status = int
  type Format = int // remove `=` to have type safety
  const A Status = 1
  const B Format = 1
  func main() {
      println(A == B)
  }

**


如果你想省略返回参数,你最好表示出来


_ = f() 比 f() 更好

**


我们用 a := []T{} 来简单初始化 slice


**


用 range 循环来进行数组或 slice 的迭代


for _, c := range a[3:7] {...} 比 for i := 3; i < 7; i++ {...} 更好

**


多行字符串用反引号(`)


**


用 _ 来跳过不用的参数


 func f(a int, _ string) {}

1

** 如果你要比较时间戳,请使用 time.Before 或 time.After ,不要使用 time.Sub 来获得 duration (持续时间),然后检查它的值。

** 带有上下文的函数第一个参数名为 ctx,形如:func foo(ctx Context, ...)

** 几个相同类型的参数定义可以用简短的方式来进行

  func f(a int, b int, s string, p string)
  func f(a, b int, s, p string)

** 一个 slice 的零值是 nil

```
var s []int
fmt.Println(s, len(s), cap(s))
if s == nil {
fmt.Println("nil!")
}
// Output:
// [] 0 0
// nil!
```
  var a []string
  b := []string{}
  fmt.Println(reflect.DeepEqual(a, []string{}))
  fmt.Println(reflect.DeepEqual(b, []string{}))
  // Output:
  // false
  // true
  • ** 不要将枚举类型与<,>,<=>=进行比较
  • 使用确定的值,不要像下面这样做:
  value := reflect.ValueOf(object)
  kind := value.Kind()
  if kind >= reflect.Chan && kind <= reflect.Slice {
    // ...
  }
  • ** 用 %+v 来打印数据的比较全的信息
  • ** 注意空结构 struct{}
  func f1() {
    var a, b struct{}
    print(&a, "\n", &b, "\n") // Prints same address
    fmt.Println(&a == &b)     // Comparison returns false
  }
  func f2() {
    var a, b struct{}
    fmt.Printf("%p\n%p\n", &a, &b) // Again, same address
    fmt.Println(&a == &b)          // ...but the comparison returns true
  }

**


例如: errors.Wrap(err, "additional message to a given error")

**


在 Go 里面要小心使用 range:


for i := range a and for i, v := range &a ,都不是 a 的副本

但是 for i, v := range a 里面的就是 a 的副本

**


从 map 读取一个不存在的 key 将不会 panic


value := map["no_key"] 将得到一个 0 值

value, ok := map["no_key"] 更好

**


不要使用原始参数进行文件操作


而不是一个八进制参数 os.MkdirAll(root, 0700)

使用此类型的预定义常量 os.FileMode

**


不要忘记为 iota 指定一种类型

    const (
      _ = iota
      testvar         // testvar 将是 int 类型
    )

vs

    type myType int
    const (
      _ myType = iota
      testvar         // testvar 将是 myType 类型
    )

不要在你不拥有的结构上使用 encoding/gob

在某些时候,结构可能会改变,而你可能会错过这一点。因此,这可能会导致很难找到 bug。

相关文章
|
2月前
|
开发框架 安全 中间件
Go语言开发小技巧&易错点100例(十二)
Go语言开发小技巧&易错点100例(十二)
31 1
|
2月前
|
Go
Go语言开发小技巧&易错点100例(十一)
Go语言开发小技巧&易错点100例(十一)
16 0
|
2月前
|
存储 Java Go
Go语言开发小技巧&易错点100例(十)
Go语言开发小技巧&易错点100例(十)
19 0
|
2月前
|
Go 开发者
Go语言开发小技巧&易错点100例(九)
Go语言开发小技巧&易错点100例(九)
15 0
|
2月前
|
JSON Go 数据格式
Go语言开发小技巧&易错点100例(八)
Go语言开发小技巧&易错点100例(八)
17 0
|
2月前
|
消息中间件 IDE Go
Go语言开发小技巧&易错点100例(七)
Go语言开发小技巧&易错点100例(七)
18 0
|
2月前
|
运维 监控 Go
Go语言开发小技巧&易错点100例(六)
Go语言开发小技巧&易错点100例(六)
20 0
|
2月前
|
Java Go
Go语言开发小技巧&易错点100例(五)
Go语言开发小技巧&易错点100例(五)
20 0
|
2月前
|
Go
Go语言开发小技巧&易错点100例(四)
Go语言开发小技巧&易错点100例(四)
22 0
|
2月前
|
JSON Go 数据格式
Go语言开发小技巧&易错点100例(三)
Go语言开发小技巧&易错点100例(三)
17 0