Page

10个go编程注意事项

1219Anson23-11-05



  1. Go 很简单,但不容易掌握

Go is simple but not easy.

简单意味着易懂,Go 语法基本上花 2 小时就能全部看完。但是要想掌握它、写好它却不容易。比如,goroutine 和 channel 该简单了吧,但是使用 channel 出错的 case 数不胜数。

之前有篇讲 Concurrency bugs 的论文《Understanding Real-World Concurrency Bugs in Go》说:尽管人们普遍认为通过 channel 来传递消息更少出错误,但是论文里研究的 bug 表明,正好相反,用 mutex 才更少出错。

  1. The bigger the interface, the weaker the abstraction

Rob Pike说:The bigger the interface, the weaker the abstraction。当一个接口的方法越多,它的抽象能力越弱。像接口 Reader/Writer 为何很强大,因为它们就只有一个方法。

他还说:Don’t design with interfaces, discover them. 意思就是只有在实现过程中发现需要 interface 时才需要定义。是自下而上的过程,而非相反。

  1. net 包和 net/http 包并没有层级关系

可以认为是两个不同的包,它们仅仅是文件位置有层级关系而已。

  1. 包名要反映这个包能提供什么能力,而不是它包含了哪些内容。

函数名反映它做了什么,而不是怎么做。虽然命名一直是编程界的难题,但不断尝试好的命名也是必要的。日常的 util, common, base 这些包名其实并不好。任何对外暴露的内容:包、函数、方法、变量都应该给出说明。

  1. nil slice 的几个特点

不分配内存。对于一个函数的返回值而言,返回 nil slice 比 emtpy slice 要更好。

在 marshal 时,nil slice 是 null,而 empty slice 是 []。因此在使用相关库函数时,要特别注意这两者的区别。

nil slice 和 empty slice 不 equal。

以下代码中前 2 个是 nil slice,后两个不是。

  1. copy 函数拷贝的元素数量是 min(len(dst), len(min))

  2. 初始化 map 时,指定一个长度

它能给 runtime 以提示,这样后续可以减少重新分配元素的开销。并且要注意:这个长度并不是说 map 只能放这么多元素,这里面有一个公式会计算。

map 的 buckets 数只会增,不会降。所以当在流量冲击后,map 的 buckets 数扩容到了一个新高度,之后即使把元素都删除了也无济于事。内存占用还是在,因为基础的 buckets 占用的内存不会少。

  1. 不要边遍历 map 边写入 key

在遍历 map 的过程中,新写入的 key 可能被遍历出来,也可能不被遍历出来,可能会与预期的行为不符,因此不要边遍历边写入。

下面这个例子输出的结果不确定:

func main() {  
    m := map[int]bool{  
        0: true,  
        1: false,  
        2: true,  
    }   
    for k, v := range m {  
        if v {  
            m[10+k] = true  
        }  
    }   
    fmt.Println(m) 
}
  1. break 可以作用于 for, select, switch

break 只能跳出一重循环,因此要注意,break 是否跳到了你预想的地方。可以用 break with label 来解决。毕竟标准库里也这样用了:

  1. for 循环加指针,老司机也会掉的坑

在 for range 循环里保存迭代变量的指针是一个非常容易犯的错误,Go 老手也会犯。原因是迭代变量至始至终都是同一个值,对它取地址得到的值也是相同的:




    来自anson博客 

    http://www.tp0.top

    2023-11-05 21:14:52