go-EffectiveGo_阅读笔记
go-EffectiveGo_阅读笔记, 在线文档: https://www.kancloud.cn/kancloud/effective/72199
程序包名
程序包使用小写,一个单词的名字;不需要使用下划线或者混合大小写。要力求简短
程序包名只是导入的缺省名字;其不需要在所有源代码中是唯一的。
另一种约定是,程序包名为其源目录的基础名;在 src/pkg/encoding/base64 中的程序包,是作为”encoding/base64” 来导入的,但是名字为 base64 ,而不是 encoding_base64 或 encodingBase64
Get方法
如果你有一个域叫
做 owner (小写,不被导出),则Get方法应该叫做 Owner (大写,被导出),而不是 GetOwner 。对于要导出的,使用大写名字,提供了区别域和方法的钩子。Set方法,如果需要,则可以叫做 SetOwner
。
接口名
单个方法的接口使用方法名加上“er”后缀来命名,或者类似的修改来构造一个施动者名词:
Reader , Writer , Formatter , CloseNotifier 等 , 以及它们所体现的函数名字。 Read , Write , Close, Flush , String 等,都具有规范的签名和含义
混合大小写
Go约定使用 MixedCaps 或者 mixedCaps 的形式,而不是下划线来书写多个单词的名字
重新声明和重新赋值
1 | f, err := os.Open(name) |
注意 err 在两条语句中都出现了。这种重复是合法的: err 是在第一条语句中被声明,而在第二条语句中 只是被重新赋值 。这意味着使用之前已经声明过的 err 变量调用f.Stat ,只会是赋给其一个新的值, 并没有开辟一个新的寄存器.
在 := 声明中,变量 v 即使已经被声明过,也可以出现,前提是:
- 该声明和 v 已有的声明在相同的作用域中(如果 v 已经在外面的作用域里被声明了,则该声明将会创建一个新的变量 §)
- 初始化中相应的值是可以被赋给 v 的
- 并且,声明中至少有其它一个变量将被声明为一个新的变量
For
Go的 for 循环类似于—但又不等同于—C的。它统一了 for 和 while ,并且没有 do-while 。有三种形式, 其中只有一个具有分号。
1 | // Like a C for |
对于字符串,range
会做更多的事情,通过解析UTF-8来拆分出单个的Unicode编码点。错误的编码会消耗一个字节,产生一个替代的符文(rune)U+FFFD。(名字(与内建类型相关联的)rune
是Go的术语,用于指定一个单独的Unicode编码点。详情参见the language specification)循环
Switch
Go的switch
要比C的更加通用。表达式不需要为常量,甚至不需要为整数,case是按照从上到下的顺序进行求值,直到找到匹配的。如果switch
没有表达式,则对true
进行匹配。因此,可以—按照语言习惯—将if
-else
-if
-else
链写成一个switch
。
1 | func unhex(c byte) byte { |
switch不会自动从一个case子句跌落到下一个case子句。但是case可以使用逗号分隔的列表。
1 | func shouldEscape(c byte) bool { |
new 和 make
new :
new(T)
会为T
类型的新项目,分配被置零的存储,并且返回它的地址,一个类型为*T
的值分配或者声明之后直接使用。在下一个片段中,
p
和v
都不需要进一步的处理便可以正确地工作。1
2p := new(SyncedBuffer) // type *SyncedBuffer
var v SyncedBuffer // type SyncedBuffermake : 它只用来创建 slice,map 和 channel,并且返回一个初始化的(而不是置零),类型为
T
的值(而不是*T
)
这些例子阐释了new
和make
之间的差别。
1 | var p *[]int = new([]int) // allocates slice structure; *p == nil; rarely useful |
空白标识符 _
空白标识符在多赋值语句中的使用
1
if _, err := os.Stat(path); os.IsNotExist(err) { // 占位
未使用的导入和变量
1
2
3
4
5import (
"fmt"
)
var _ = fmt.Printf // 只为 引入 fmt 包副作用式导入
1
import _ "net/http/pprof" // 导入包, 只为让包内执行 init 函数
Channels
如果在创建channel时提供一个可选的整型参数,会设置该channel的缓冲区大小
1 | cs := make(chan *os.File, 100) |
如果channel是无缓冲的,发送方会一直阻塞直到接收方将数据取出。
如果channel带有缓冲区,发送方会一直阻塞直到数据被拷贝到缓冲区;如果缓冲区已满,则发送方只能在接收方取走数据后才能从阻塞状态恢复。
并发控制
1 | var sem = make(chan int, MaxOutstanding) // MaxOutstanding 为并发上限 |