Bug-risks是代码中的问题,会在生产中引起错误和破坏。漏洞是代码中的一个缺陷,会产生不想要的或不正确的结果。由于不良的编码实践,缺乏版本控制的最佳实践,需求的误传,以及不现实的开发时间安排,代码经常会出现bug-风险。在这篇文章中,让我们来看看到目前为止我们所看到的围棋中几个常见的bug-风险。
1.无限的递归调用
递归调用自身的函数需要有一个退出条件。否则,它将永远地递归,直到系统的内存耗尽。
这个问题可能是由常见的错误引起的,比如忘记添加退出条件。它也可能 "故意 "发生。一些语言有尾部调用优化,这使得某些无限递归的调用可以安全使用。尾部调用优化允许你避免为一个函数分配一个新的堆栈框架,因为调用函数将返回它从被调用函数得到的值。最常见的用途是尾部递归,为利用尾部调用优化而编写的递归函数可以使用恒定的堆栈空间。然而,Go并没有实现尾部调用优化,你最终会耗尽内存。然而,这个问题并不适用于催生新的goroutine。
2.赋值到nil map
在添加任何元素之前,需要用make 函数(或map literal)来初始化一个map。一个新的、空的地图值是使用内置函数make ,它以map 类型和一个可选的容量提示作为参数。
make(map[string]int)
make(map[string]int, 100)
初始容量并不约束其大小:地图的增长是为了适应其中存储的项目数量,但nil 地图除外。一个nil 地图等同于一个空地图,只是不能添加任何元素。
不好的模式:
var countedData map[string][]ChartElement
好的模式:
countedData := make(map[string][]ChartElement)
推荐阅读。Go:对nil地图中的条目进行赋值
3.方法修改了接收器
一个修改非指针接收器值的方法可能会产生不必要的后果。这是一个错误的风险,因为该方法可能会改变方法内部的接收器的值,但它不会反映在原始值中。为了传播这种变化,接收器必须是一个指针。
4.可能不需要的值被用于goroutine中
循环中的范围变量在每次迭代时都会被重复使用;因此,在循环中创建的goroutine将从上层范围指向范围变量。这样一来,goroutine就可以用一个不想要的值来使用这个变量。
在下面的例子中,在goroutine中使用的index和value的值来自外层作用域。因为goroutine是异步运行的,所以index和value的值可能(而且通常是)与预期的值不同。
mySlice := []string{"A", "B", "C"}
for index, value := range mySlice {
go func() {
fmt.Printf("Index: %d\n", index)
fmt.Printf("Value: %s\n", value)
}()
}
为了克服这个问题,必须创建一个局部作用域,就像下面的例子一样。
mySlice := []string{"A", "B", "C"}
for index, value := range mySlice {
index := index
value := value
go func() {
fmt.Printf("Index: %d\n", index)
fmt.Printf("Value: %s\n", value)
}()
}
另一种处理方式是将值作为args传递给goroutines。
mySlice := []string{"A", "B", "C"}
for index, value := range mySlice {
go func(index int, value string) {
fmt.Printf("Index: %d\n", index)
fmt.Printf("Value: %s\n", value)
}(index, value)
}
5.在检查可能出现的错误之前推迟Close
defer 这是Go开发者中的一个常见模式,为实现io.Closer 接口的值,Close() 方法。例如,在打开一个文件时。
f, err := os.Open("/tmp/file.md")
if err != nil {
return err
}
defer f.Close()
但是这种模式对于可写文件来说是有害的,因为推迟一个函数的调用会忽略其返回值,而Close() 方法可能会返回错误。例如,如果你向文件写了数据,那么在你调用Close 的时候,它可能已经被缓存在内存中而没有被刷到磁盘上。这个错误应该被明确地处理。
虽然你可以完全不使用defer ,但你需要记住在他们的工作完成后关闭文件。一个更好的方法是defer 一个封装函数,就像下面的例子。
f, err := os.Open("/tmp/file.md")
if err != nil {
return err
}
defer func() {
closeErr := f.Close()
if closeErr != nil {
if err == nil {
err = closeErr
} else {
log.Println("Error occured while closing the file :", closeErr)
}
}
}()
return err