Golang 错误使用

变量作用域与同名变量,导致的错误

var remember bool = false
if something {
   remember := true //错误
 - 短声明时声明了外面的同名变量 - 使用错误
}
// 使用remember  -- remember 永远不会等于 true

注意 for 循环或者 函数的返回参数时,还有 defer 中,都可能有这样的错误

字符串的误用

Golang 的字符串是不可变的,每次的字符串拼接都是效率低下的,会导致大量的内存开销和拷贝。

应该使用一个字符数组代替字符串,将字符串写入一个缓存中,如:

var b bytes.Buffer
for condition {
    b.WriteString(str) // 将字符串str写入缓存buffer
}
return b.String()

Tips: 由于Golang 自带的优化处理,当循环大于 15 次,效率最高

defer 仅在函数返回时才执行,如下就是一个错误例子

for _, file := range files {
  if f, err = os.Open(file); err != nil {
    return
  }
  // 这是错误的方式,当循环结束时文件没有关闭
  // defer f.Close()
 
  // 对文件进行操作
  f.Process(data)
  // 正确的做法是,显示的调用 close 关闭文件
  f.Close()
}

不需要将一个指向切片的指针传递给函数

切片实际是一个指向数组的指针,在传递给函数时,传递的就是指向变量的指针,而不是传递数据的拷贝,可以直接在函数内改变此变量

不要使用一个指针指向一个接口类型,因为它自己就是一个指针

package main
import (
  “fmt”
)
type nexter interface {
  next() byte
}
func nextFew1(n nexter, num int) []byte {
  var b []byte
  for i:=0; i < num; i++ {
    b[i] = n.next()
  }
  return b
}
func nextFew2(n *nexter, num int) []byte {
  var b []byte
  for i:=0; i < num; i++ {
    b[i] = n.next() // 编译错误:n.next未定义(*nexter类型没有next成员或next方法)
  }
  return b
}
func main() {
  fmt.Println(“Hello World!”)
}

值类型一直是分配在栈上,如果误用了其指针则会导致内存额外的分配,Go可能会把其移动到堆上

闭包与协程的使用,以下代码输出数组的索引或值,各版本对比

var values = [5]int{10, 11, 12, 13, 14}
func main() {
// 版本A:
for ix := range values { // ix是索引值
  func() {
    fmt.Print(ix, " ")
 // 0 1 2 3 4
  }() // 调用闭包打印每个索引值
}
fmt.Println()
// 版本B: 和A版本类似,但是通过调用闭包作为一个协程
for ix := range values {
  go func() {
    fmt.Print(ix, " ")
 // 4 4 4 4 4
  }()
}
fmt.Println()
time.Sleep(5e9)
// 版本C: 正确的处理方式
for ix := range values {
  go func(ix interface{}) {
    fmt.Print(ix, " ")
 // out : 1 0 3 4 2 ; 顺序可蛮,取决于 协程执行时间
  }(ix)
}
fmt.Println()
time.Sleep(5e9)
// 版本D: 输出值:
for ix := range values {
  val := values[ix]
  go func() {
    fmt.Print(val, " ")
 // out: 10 11 12 13 14
  }()
}
time.Sleep(1e9)
}

错误检测代码的优化使用,如下这样可以很明确分辨出错误检测、错误通知、正常逻辑

func httpRequestHandler(w http.ResponseWriter, req *http.Request) {
  err := func () error {
 // 以闭包的形式,返回错误信息,很明确的显示哪些是要检查错误的
    if req.Method != "GET" {
      return errors.New("expected GET")
    }
    if input := parseInput(req); input != "command" {
      return errors.New("malformed command")
    }
    // 可以在此进行其他的错误检测
  } ()
  if err != nil {
    w.WriteHeader(400)
    io.WriteString(w, err)
    return
  }
  doSomething() ...
}